mirror of
https://github.com/unisonweb/unison.git
synced 2024-09-17 13:27:30 +03:00
Merge remote-tracking branch 'origin/trunk' into pchiusano/ormolu_prs
# Conflicts: # parser-typechecker/src/Unison/Runtime/Foreign.hs
This commit is contained in:
commit
7e37991a96
@ -253,7 +253,8 @@ builtinTypesSrc =
|
||||
B' "ImmutableArray" CT.Data,
|
||||
B' "MutableArray" CT.Data,
|
||||
B' "ImmutableByteArray" CT.Data,
|
||||
B' "MutableByteArray" CT.Data
|
||||
B' "MutableByteArray" CT.Data,
|
||||
B' "Char.Class" CT.Data
|
||||
]
|
||||
|
||||
-- rename these to "builtin" later, when builtin means intrinsic as opposed to
|
||||
@ -665,7 +666,32 @@ builtinsSrc =
|
||||
B "Scope.bytearray" . forall1 "s" $ \s ->
|
||||
nat --> Type.effect1 () (scopet s) (mbytearrayt (scopet s)),
|
||||
B "Scope.bytearrayOf" . forall1 "s" $ \s ->
|
||||
nat --> nat --> Type.effect1 () (scopet s) (mbytearrayt (scopet s))
|
||||
nat --> nat --> Type.effect1 () (scopet s) (mbytearrayt (scopet s)),
|
||||
B "Char.Class.any" charClass,
|
||||
B "Char.Class.not" $ charClass --> charClass,
|
||||
B "Char.Class.and" $ charClass --> charClass --> charClass,
|
||||
B "Char.Class.or" $ charClass --> charClass --> charClass,
|
||||
B "Char.Class.range" $ char --> char --> charClass,
|
||||
B "Char.Class.anyOf" $ list char --> charClass,
|
||||
B "Char.Class.alphanumeric" charClass,
|
||||
B "Char.Class.upper" charClass,
|
||||
B "Char.Class.lower" charClass,
|
||||
B "Char.Class.whitespace" charClass,
|
||||
B "Char.Class.control" charClass,
|
||||
B "Char.Class.printable" charClass,
|
||||
B "Char.Class.mark" charClass,
|
||||
B "Char.Class.number" charClass,
|
||||
B "Char.Class.punctuation" charClass,
|
||||
B "Char.Class.symbol" charClass,
|
||||
B "Char.Class.separator" charClass,
|
||||
B "Char.Class.letter" charClass,
|
||||
B "Char.Class.is" $
|
||||
charClass
|
||||
--> char
|
||||
--> boolean,
|
||||
B
|
||||
"Text.patterns.char"
|
||||
$ charClass --> pat text
|
||||
]
|
||||
++
|
||||
-- avoid name conflicts with Universal == < > <= >=
|
||||
@ -1041,5 +1067,8 @@ stm = Type.effect1 () (Type.ref () Type.stmRef)
|
||||
tvar a = Type.ref () Type.tvarRef `app` a
|
||||
pat a = Type.ref () Type.patternRef `app` a
|
||||
|
||||
charClass :: Type
|
||||
charClass = Type.ref () Type.charClassRef
|
||||
|
||||
timeSpec :: Type
|
||||
timeSpec = Type.ref () Type.timeSpecRef
|
||||
|
@ -153,6 +153,7 @@ import Unison.Runtime.Foreign.Function
|
||||
import Unison.Runtime.Stack (Closure)
|
||||
import qualified Unison.Runtime.Stack as Closure
|
||||
import Unison.Symbol
|
||||
import Unison.Type (charRef)
|
||||
import qualified Unison.Type as Ty
|
||||
import qualified Unison.Util.Bytes as Bytes
|
||||
import Unison.Util.EnumContainers as EC
|
||||
@ -1587,6 +1588,16 @@ wordDirect wordType instr =
|
||||
where
|
||||
(b1, ub1) = fresh
|
||||
|
||||
-- Nat -> Bool
|
||||
boxWordToBool :: Reference -> ForeignOp
|
||||
boxWordToBool wordType instr =
|
||||
([BX, BX],)
|
||||
. TAbss [b1, w1]
|
||||
. unbox w1 wordType uw1
|
||||
$ TLetD result UN (TFOp instr [b1, uw1]) (boolift result)
|
||||
where
|
||||
(b1, w1, uw1, result) = fresh
|
||||
|
||||
-- Nat -> Nat -> c
|
||||
wordWordDirect :: Reference -> Reference -> ForeignOp
|
||||
wordWordDirect word1 word2 instr =
|
||||
@ -2332,7 +2343,7 @@ declareForeigns = do
|
||||
$ \(hs, n) ->
|
||||
maybe mempty Bytes.fromArray <$> SYS.recv hs n
|
||||
|
||||
declareForeign Tracked "IO.kill.impl.v3" boxTo0 $ mkForeignIOF killThread
|
||||
declareForeign Tracked "IO.kill.impl.v3" boxToEF0 $ mkForeignIOF killThread
|
||||
|
||||
declareForeign Tracked "IO.delay.impl.v3" natToEFUnit $
|
||||
mkForeignIOF threadDelay
|
||||
@ -2900,32 +2911,32 @@ declareForeigns = do
|
||||
declareForeign Untracked "Text.patterns.literal" boxDirect . mkForeign $
|
||||
\txt -> evaluate . TPat.cpattern $ TPat.Literal txt
|
||||
declareForeign Untracked "Text.patterns.digit" direct . mkForeign $
|
||||
let v = TPat.cpattern TPat.Digit in \() -> pure v
|
||||
let v = TPat.cpattern (TPat.Char (TPat.CharRange '0' '9')) in \() -> pure v
|
||||
declareForeign Untracked "Text.patterns.letter" direct . mkForeign $
|
||||
let v = TPat.cpattern TPat.Letter in \() -> pure v
|
||||
let v = TPat.cpattern (TPat.Char (TPat.CharClass TPat.Letter)) in \() -> pure v
|
||||
declareForeign Untracked "Text.patterns.space" direct . mkForeign $
|
||||
let v = TPat.cpattern TPat.Space in \() -> pure v
|
||||
let v = TPat.cpattern (TPat.Char (TPat.CharClass TPat.Whitespace)) in \() -> pure v
|
||||
declareForeign Untracked "Text.patterns.punctuation" direct . mkForeign $
|
||||
let v = TPat.cpattern TPat.Punctuation in \() -> pure v
|
||||
let v = TPat.cpattern (TPat.Char (TPat.CharClass TPat.Punctuation)) in \() -> pure v
|
||||
declareForeign Untracked "Text.patterns.anyChar" direct . mkForeign $
|
||||
let v = TPat.cpattern TPat.AnyChar in \() -> pure v
|
||||
let v = TPat.cpattern (TPat.Char TPat.Any) in \() -> pure v
|
||||
declareForeign Untracked "Text.patterns.eof" direct . mkForeign $
|
||||
let v = TPat.cpattern TPat.Eof in \() -> pure v
|
||||
let ccd = wordWordDirect Ty.charRef Ty.charRef
|
||||
declareForeign Untracked "Text.patterns.charRange" ccd . mkForeign $
|
||||
\(beg, end) -> evaluate . TPat.cpattern $ TPat.CharRange beg end
|
||||
\(beg, end) -> evaluate . TPat.cpattern . TPat.Char $ TPat.CharRange beg end
|
||||
declareForeign Untracked "Text.patterns.notCharRange" ccd . mkForeign $
|
||||
\(beg, end) -> evaluate . TPat.cpattern $ TPat.NotCharRange beg end
|
||||
\(beg, end) -> evaluate . TPat.cpattern . TPat.Char . TPat.Not $ TPat.CharRange beg end
|
||||
declareForeign Untracked "Text.patterns.charIn" boxDirect . mkForeign $ \ccs -> do
|
||||
cs <- for ccs $ \case
|
||||
Closure.DataU1 _ _ i -> pure (toEnum i)
|
||||
_ -> die "Text.patterns.charIn: non-character closure"
|
||||
evaluate . TPat.cpattern $ TPat.CharIn cs
|
||||
evaluate . TPat.cpattern . TPat.Char $ TPat.CharSet cs
|
||||
declareForeign Untracked "Text.patterns.notCharIn" boxDirect . mkForeign $ \ccs -> do
|
||||
cs <- for ccs $ \case
|
||||
Closure.DataU1 _ _ i -> pure (toEnum i)
|
||||
_ -> die "Text.patterns.notCharIn: non-character closure"
|
||||
evaluate . TPat.cpattern $ TPat.NotCharIn cs
|
||||
evaluate . TPat.cpattern . TPat.Char . TPat.Not $ TPat.CharSet cs
|
||||
declareForeign Untracked "Pattern.many" boxDirect . mkForeign $
|
||||
\(TPat.CP p _) -> evaluate . TPat.cpattern $ TPat.Many p
|
||||
declareForeign Untracked "Pattern.capture" boxDirect . mkForeign $
|
||||
@ -2945,6 +2956,32 @@ declareForeigns = do
|
||||
declareForeign Untracked "Pattern.isMatch" boxBoxToBool . mkForeign $
|
||||
\(TPat.CP _ matcher, input :: Text) -> pure . isJust $ matcher input
|
||||
|
||||
declareForeign Untracked "Char.Class.any" direct . mkForeign $ \() -> pure TPat.Any
|
||||
declareForeign Untracked "Char.Class.not" boxDirect . mkForeign $ pure . TPat.Not
|
||||
declareForeign Untracked "Char.Class.and" boxBoxDirect . mkForeign $ \(a, b) -> pure $ TPat.Intersect a b
|
||||
declareForeign Untracked "Char.Class.or" boxBoxDirect . mkForeign $ \(a, b) -> pure $ TPat.Union a b
|
||||
declareForeign Untracked "Char.Class.range" (wordWordDirect charRef charRef) . mkForeign $ \(a, b) -> pure $ TPat.CharRange a b
|
||||
declareForeign Untracked "Char.Class.anyOf" boxDirect . mkForeign $ \ccs -> do
|
||||
cs <- for ccs $ \case
|
||||
Closure.DataU1 _ _ i -> pure (toEnum i)
|
||||
_ -> die "Text.patterns.charIn: non-character closure"
|
||||
evaluate $ TPat.CharSet cs
|
||||
declareForeign Untracked "Char.Class.alphanumeric" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.AlphaNum)
|
||||
declareForeign Untracked "Char.Class.upper" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Upper)
|
||||
declareForeign Untracked "Char.Class.lower" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Lower)
|
||||
declareForeign Untracked "Char.Class.whitespace" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Whitespace)
|
||||
declareForeign Untracked "Char.Class.control" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Control)
|
||||
declareForeign Untracked "Char.Class.printable" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Printable)
|
||||
declareForeign Untracked "Char.Class.mark" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.MarkChar)
|
||||
declareForeign Untracked "Char.Class.number" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Number)
|
||||
declareForeign Untracked "Char.Class.punctuation" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Punctuation)
|
||||
declareForeign Untracked "Char.Class.symbol" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Symbol)
|
||||
declareForeign Untracked "Char.Class.separator" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Separator)
|
||||
declareForeign Untracked "Char.Class.letter" direct . mkForeign $ \() -> pure (TPat.CharClass TPat.Letter)
|
||||
declareForeign Untracked "Char.Class.is" (boxWordToBool charRef) . mkForeign $ \(cl, c) -> evaluate $ TPat.charPatternPred cl c
|
||||
declareForeign Untracked "Text.patterns.char" boxDirect . mkForeign $ \c ->
|
||||
let v = TPat.cpattern (TPat.Char c) in pure v
|
||||
|
||||
type RW = PA.PrimState IO
|
||||
|
||||
checkedRead ::
|
||||
|
@ -1,5 +1,6 @@
|
||||
{-# LANGUAGE BangPatterns #-}
|
||||
{-# LANGUAGE GADTs #-}
|
||||
{-# LANGUAGE InstanceSigs #-}
|
||||
{-# LANGUAGE PatternGuards #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
@ -36,7 +37,7 @@ import Unison.Symbol (Symbol)
|
||||
import qualified Unison.Type as Ty
|
||||
import Unison.Util.Bytes (Bytes)
|
||||
import Unison.Util.Text (Text)
|
||||
import Unison.Util.Text.Pattern (CPattern)
|
||||
import Unison.Util.Text.Pattern (CPattern, CharPattern)
|
||||
import Unsafe.Coerce
|
||||
|
||||
data Foreign where
|
||||
@ -111,6 +112,14 @@ cpatCmp :: CPattern -> CPattern -> Ordering
|
||||
cpatCmp l r = compare l r
|
||||
{-# NOINLINE cpatCmp #-}
|
||||
|
||||
charClassEq :: CharPattern -> CharPattern -> Bool
|
||||
charClassEq l r = l == r
|
||||
{-# NOINLINE charClassEq #-}
|
||||
|
||||
charClassCmp :: CharPattern -> CharPattern -> Ordering
|
||||
charClassCmp = compare
|
||||
{-# NOINLINE charClassCmp #-}
|
||||
|
||||
tylEq :: Reference -> Reference -> Bool
|
||||
tylEq r l = r == l
|
||||
{-# NOINLINE tylEq #-}
|
||||
@ -144,6 +153,7 @@ ref2eq r
|
||||
| r == Ty.mbytearrayRef = Just $ promote mbarrEq
|
||||
| r == Ty.ibytearrayRef = Just $ promote barrEq
|
||||
| r == Ty.patternRef = Just $ promote cpatEq
|
||||
| r == Ty.charClassRef = Just $ promote charClassEq
|
||||
| otherwise = Nothing
|
||||
|
||||
ref2cmp :: Reference -> Maybe (a -> b -> Ordering)
|
||||
@ -155,6 +165,7 @@ ref2cmp r
|
||||
| r == Ty.threadIdRef = Just $ promote tidCmp
|
||||
| r == Ty.ibytearrayRef = Just $ promote barrCmp
|
||||
| r == Ty.patternRef = Just $ promote cpatCmp
|
||||
| r == Ty.charClassRef = Just $ promote charClassCmp
|
||||
| otherwise = Nothing
|
||||
|
||||
instance Eq Foreign where
|
||||
@ -194,7 +205,9 @@ maybeUnwrapForeign rt (Wrap r e)
|
||||
class BuiltinForeign f where
|
||||
foreignRef :: Tagged f Reference
|
||||
|
||||
instance BuiltinForeign Text where foreignRef = Tagged Ty.textRef
|
||||
instance BuiltinForeign Text where
|
||||
foreignRef :: Tagged Text Reference
|
||||
foreignRef = Tagged Ty.textRef
|
||||
|
||||
instance BuiltinForeign Bytes where foreignRef = Tagged Ty.bytesRef
|
||||
|
||||
@ -227,7 +240,7 @@ instance BuiltinForeign TimeSpec where foreignRef = Tagged Ty.timeSpecRef
|
||||
|
||||
data HashAlgorithm where
|
||||
-- Reference is a reference to the hash algorithm
|
||||
HashAlgorithm :: (Hash.HashAlgorithm a) => Reference -> a -> HashAlgorithm
|
||||
HashAlgorithm :: Hash.HashAlgorithm a => Reference -> a -> HashAlgorithm
|
||||
|
||||
newtype Tls = Tls TLS.Context
|
||||
|
||||
@ -238,15 +251,18 @@ instance BuiltinForeign HashAlgorithm where foreignRef = Tagged Ty.hashAlgorithm
|
||||
instance BuiltinForeign CPattern where
|
||||
foreignRef = Tagged Ty.patternRef
|
||||
|
||||
wrapBuiltin :: forall f. (BuiltinForeign f) => f -> Foreign
|
||||
instance BuiltinForeign CharPattern where
|
||||
foreignRef = Tagged Ty.charClassRef
|
||||
|
||||
wrapBuiltin :: forall f. BuiltinForeign f => f -> Foreign
|
||||
wrapBuiltin x = Wrap r x
|
||||
where
|
||||
Tagged r = foreignRef :: Tagged f Reference
|
||||
|
||||
unwrapBuiltin :: (BuiltinForeign f) => Foreign -> f
|
||||
unwrapBuiltin :: BuiltinForeign f => Foreign -> f
|
||||
unwrapBuiltin (Wrap _ x) = unsafeCoerce x
|
||||
|
||||
maybeUnwrapBuiltin :: forall f. (BuiltinForeign f) => Foreign -> Maybe f
|
||||
maybeUnwrapBuiltin :: forall f. BuiltinForeign f => Foreign -> Maybe f
|
||||
maybeUnwrapBuiltin (Wrap r x)
|
||||
| r == r0 = Just (unsafeCoerce x)
|
||||
| otherwise = Nothing
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
module Unison.Util.Text.Pattern where
|
||||
|
||||
import Data.Char (isDigit, isLetter, isPunctuation, isSpace)
|
||||
import Data.Char (isAlphaNum, isControl, isLetter, isLower, isMark, isNumber, isPrint, isPunctuation, isSeparator, isSpace, isSymbol, isUpper)
|
||||
import qualified Data.Text as DT
|
||||
import Unison.Util.Text (Text)
|
||||
import qualified Unison.Util.Text as Text
|
||||
@ -13,18 +13,35 @@ data Pattern
|
||||
| Capture Pattern -- capture all the text consumed by the inner pattern, discarding its subcaptures
|
||||
| Many Pattern -- zero or more repetitions (at least 1 can be written: Join [p, Many p])
|
||||
| Replicate Int Int Pattern -- m to n occurrences of a pattern, optional = 0-1
|
||||
| AnyChar -- consume a single char
|
||||
| Eof -- succeed if given the empty text, fail otherwise
|
||||
| Literal Text -- succeed if input starts with the given text, advance by that text
|
||||
| CharRange Char Char -- consume 1 char in the given range, or fail
|
||||
| CharIn [Char] -- consume 1 char in the given set, or fail
|
||||
| NotCharIn [Char] -- consume 1 char NOT in the given set, or fail
|
||||
| NotCharRange Char Char -- consume 1 char NOT in the given range, or fail
|
||||
| Digit -- consume 1 digit (according to Char.isDigit)
|
||||
| Letter -- consume 1 letter (according to Char.isLetter)
|
||||
| Space -- consume 1 space character (according to Char.isSpace)
|
||||
| Punctuation -- consume 1 punctuation char (according to Char.isPunctuation)
|
||||
deriving (Eq, Ord)
|
||||
| Char CharPattern -- succeed if input starts with a char matching the given pattern, advance by 1 char
|
||||
deriving (Show, Eq, Ord)
|
||||
|
||||
data CharPattern
|
||||
= Any -- any char
|
||||
| Not CharPattern -- negation of the given pattern
|
||||
| Union CharPattern CharPattern -- match if either pattern matches
|
||||
| Intersect CharPattern CharPattern -- match if both patterns match
|
||||
| CharRange Char Char -- match if char is in the given range
|
||||
| CharSet [Char] -- match if char is in the given set
|
||||
| CharClass CharClass -- match if char is in the given class
|
||||
deriving (Show, Eq, Ord)
|
||||
|
||||
data CharClass
|
||||
= AlphaNum -- alphabetic or numeric characters
|
||||
| Upper -- uppercase alphabetic characters
|
||||
| Lower -- lowercase alphabetic characters
|
||||
| Whitespace -- whitespace characters (space, tab, newline, etc.)
|
||||
| Control -- non-printing control characters
|
||||
| Printable -- letters, numbers, punctuation, symbols, spaces
|
||||
| MarkChar -- accents, diacritics, etc.
|
||||
| Number -- numeric characters in any script
|
||||
| Punctuation -- connectors, brackets, quotes
|
||||
| Symbol -- symbols (math, currency, etc.)
|
||||
| Separator -- spaces, line separators, paragraph separators
|
||||
| Letter -- letters in any script
|
||||
deriving (Show, Eq, Ord)
|
||||
|
||||
-- Wrapper type. Holds a pattern together with its compilation. This is used as
|
||||
-- the semantic value of a unison `Pattern a`. Laziness avoids building the
|
||||
@ -98,13 +115,13 @@ compile (Literal txt) !err !success = go
|
||||
go acc t
|
||||
| Text.take (Text.size txt) t == txt = success acc (Text.drop (Text.size txt) t)
|
||||
| otherwise = err acc t
|
||||
compile AnyChar !err !success = go
|
||||
compile (Char Any) !err !success = go
|
||||
where
|
||||
go acc t = case Text.drop 1 t of
|
||||
rem
|
||||
| Text.size t > Text.size rem -> success acc rem
|
||||
| otherwise -> err acc rem
|
||||
compile (Capture (Many AnyChar)) !_ !success = \acc t -> success (pushCapture t acc) Text.empty
|
||||
compile (Capture (Many (Char Any))) !_ !success = \acc t -> success (pushCapture t acc) Text.empty
|
||||
compile (Capture c) !err !success = go
|
||||
where
|
||||
err' _ _ acc0 t0 = err acc0 t0
|
||||
@ -122,38 +139,15 @@ compile (Join ps) !err !success = go ps
|
||||
let pc = compile p err psc
|
||||
psc = compile (Join ps) err success
|
||||
in pc
|
||||
compile (NotCharIn cs) !err !success = go
|
||||
compile (Char cp) !err !success = go
|
||||
where
|
||||
ok = charNotInPred cs
|
||||
ok = charPatternPred cp
|
||||
go acc t = case Text.uncons t of
|
||||
Just (ch, rem) | ok ch -> success acc rem
|
||||
_ -> err acc t
|
||||
compile (CharIn cs) !err !success = go
|
||||
where
|
||||
ok = charInPred cs
|
||||
go acc t = case Text.uncons t of
|
||||
Just (ch, rem) | ok ch -> success acc rem
|
||||
_ -> err acc t
|
||||
compile (CharRange c1 c2) !err !success = go
|
||||
where
|
||||
go acc t = case Text.uncons t of
|
||||
Just (ch, rem) | ch >= c1 && ch <= c2 -> success acc rem
|
||||
_ -> err acc t
|
||||
compile (NotCharRange c1 c2) !err !success = go
|
||||
where
|
||||
go acc t = case Text.uncons t of
|
||||
Just (ch, rem) | not (ch >= c1 && ch <= c2) -> success acc rem
|
||||
_ -> err acc t
|
||||
compile (Many p) !_ !success = case p of
|
||||
AnyChar -> (\acc _ -> success acc Text.empty)
|
||||
CharIn cs -> walker (charInPred cs)
|
||||
NotCharIn cs -> walker (charNotInPred cs)
|
||||
CharRange c1 c2 -> walker (\ch -> ch >= c1 && ch <= c2)
|
||||
NotCharRange c1 c2 -> walker (\ch -> ch < c1 || ch > c2)
|
||||
Digit -> walker isDigit
|
||||
Letter -> walker isLetter
|
||||
Punctuation -> walker isPunctuation
|
||||
Space -> walker isSpace
|
||||
Char Any -> (\acc _ -> success acc Text.empty)
|
||||
Char cp -> walker (charPatternPred cp)
|
||||
p -> go
|
||||
where
|
||||
go = compile p success success'
|
||||
@ -169,25 +163,18 @@ compile (Many p) !_ !success = case p of
|
||||
rem
|
||||
| DT.null rem -> go acc t
|
||||
| otherwise ->
|
||||
-- moving the remainder to the root of the tree is much more efficient
|
||||
-- since the next uncons will be O(1) rather than O(log n)
|
||||
-- this can't unbalance the tree too badly since these promoted chunks
|
||||
-- are being consumed and will get removed by a subsequent uncons
|
||||
success acc (Text.appendUnbalanced (Text.fromText rem) t)
|
||||
-- moving the remainder to the root of the tree is much more efficient
|
||||
-- since the next uncons will be O(1) rather than O(log n)
|
||||
-- this can't unbalance the tree too badly since these promoted chunks
|
||||
-- are being consumed and will get removed by a subsequent uncons
|
||||
success acc (Text.appendUnbalanced (Text.fromText rem) t)
|
||||
{-# INLINE walker #-}
|
||||
compile (Replicate m n p) !err !success = case p of
|
||||
AnyChar -> \acc t ->
|
||||
Char Any -> \acc t ->
|
||||
if Text.size t < m
|
||||
then err acc t
|
||||
else success acc (Text.drop n t)
|
||||
CharIn cs -> dropper (charInPred cs)
|
||||
NotCharIn cs -> dropper (charNotInPred cs)
|
||||
CharRange c1 c2 -> dropper (\ch -> ch >= c1 && c1 <= c2)
|
||||
NotCharRange c1 c2 -> dropper (\ch -> ch < c1 || ch > c2)
|
||||
Digit -> dropper isDigit
|
||||
Letter -> dropper isLetter
|
||||
Punctuation -> dropper isPunctuation
|
||||
Space -> dropper isSpace
|
||||
Char cp -> dropper (charPatternPred cp)
|
||||
_ -> try "Replicate" (go1 m) err (go2 (n - m))
|
||||
where
|
||||
go1 0 = \_err success stk rem -> success stk rem
|
||||
@ -198,26 +185,6 @@ compile (Replicate m n p) !err !success = case p of
|
||||
dropper ok acc t
|
||||
| (i, rest) <- Text.dropWhileMax ok n t, i >= m = success acc rest
|
||||
| otherwise = err acc t
|
||||
compile Digit !err !success = go
|
||||
where
|
||||
go acc t = case Text.uncons t of
|
||||
Just (ch, rem) | isDigit ch -> success acc rem
|
||||
_ -> err acc t
|
||||
compile Letter !err !success = go
|
||||
where
|
||||
go acc t = case Text.uncons t of
|
||||
Just (ch, rem) | isLetter ch -> success acc rem
|
||||
_ -> err acc t
|
||||
compile Punctuation !err !success = go
|
||||
where
|
||||
go acc t = case Text.uncons t of
|
||||
Just (ch, rem) | isPunctuation ch -> success acc rem
|
||||
_ -> err acc t
|
||||
compile Space !err !success = go
|
||||
where
|
||||
go acc t = case Text.uncons t of
|
||||
Just (ch, rem) | isSpace ch -> success acc rem
|
||||
_ -> err acc t
|
||||
|
||||
charInPred, charNotInPred :: [Char] -> Char -> Bool
|
||||
charInPred [] = const False
|
||||
@ -225,6 +192,29 @@ charInPred (c : chs) = let ok = charInPred chs in \ci -> ci == c || ok ci
|
||||
charNotInPred [] = const True
|
||||
charNotInPred (c : chs) = let ok = charNotInPred chs in (\ci -> ci /= c && ok ci)
|
||||
|
||||
charPatternPred :: CharPattern -> Char -> Bool
|
||||
charPatternPred Any = const True
|
||||
charPatternPred (Not cp) = let notOk = charPatternPred cp in \ci -> not (notOk ci)
|
||||
charPatternPred (Union cp1 cp2) = let ok1 = charPatternPred cp1; ok2 = charPatternPred cp2 in \ci -> ok1 ci || ok2 ci
|
||||
charPatternPred (Intersect cp1 cp2) = let ok1 = charPatternPred cp1; ok2 = charPatternPred cp2 in \ci -> ok1 ci && ok2 ci
|
||||
charPatternPred (CharRange c1 c2) = \ci -> ci >= c1 && ci <= c2
|
||||
charPatternPred (CharSet cs) = charInPred cs
|
||||
charPatternPred (CharClass cc) = charClassPred cc
|
||||
|
||||
charClassPred :: CharClass -> Char -> Bool
|
||||
charClassPred AlphaNum = isAlphaNum
|
||||
charClassPred Upper = isUpper
|
||||
charClassPred Lower = isLower
|
||||
charClassPred Whitespace = isSpace
|
||||
charClassPred Control = isControl
|
||||
charClassPred Printable = isPrint
|
||||
charClassPred MarkChar = isMark
|
||||
charClassPred Number = isNumber
|
||||
charClassPred Punctuation = isPunctuation
|
||||
charClassPred Symbol = isSymbol
|
||||
charClassPred Separator = isSeparator
|
||||
charClassPred Letter = isLetter
|
||||
|
||||
-- runs c and if it fails, restores state to what it was before
|
||||
try :: String -> Compiled r -> Compiled r
|
||||
try msg c err success stk rem =
|
||||
|
@ -4,6 +4,7 @@ module Unison.Test.Util.Text where
|
||||
|
||||
import Control.Monad
|
||||
import Data.List (foldl', unfoldr)
|
||||
import Data.Maybe (isNothing)
|
||||
import qualified Data.Text as T
|
||||
import EasyTest
|
||||
import qualified Unison.Util.Rope as R
|
||||
@ -104,36 +105,36 @@ test =
|
||||
ok,
|
||||
scope "patterns" $ do
|
||||
expect' (P.run P.Eof "" == Just ([], ""))
|
||||
expect' (P.run P.AnyChar "a" == Just ([], ""))
|
||||
expect' (P.run (P.CharRange 'a' 'z') "a" == Just ([], ""))
|
||||
expect' (P.run (P.NotCharRange 'a' 'z') "a" == Nothing)
|
||||
expect' (P.run (P.Or (P.NotCharRange 'a' 'z') P.AnyChar) "abc" == Just ([], "bc"))
|
||||
expect' (P.run (P.Char P.Any) "a" == Just ([], ""))
|
||||
expect' (P.run (P.Char (P.CharRange 'a' 'z')) "a" == Just ([], ""))
|
||||
expect' . isNothing $ P.run (P.Char (P.Not (P.CharRange 'a' 'z'))) "a"
|
||||
expect' (P.run (P.Or (P.Char (P.Not (P.CharRange 'a' 'z'))) (P.Char P.Any)) "abc" == Just ([], "bc"))
|
||||
-- this shows that we ignore subcaptures
|
||||
expect' (P.run (P.Join [P.Capture (P.Join [P.Capture P.AnyChar, P.Capture P.AnyChar]), P.AnyChar]) "abcdef" == Just (["ab"], "def"))
|
||||
expect' (P.run (P.CharIn "0123") "3ab" == Just ([], "ab"))
|
||||
expect' (P.run (P.NotCharIn "0123") "a3b" == Just ([], "3b"))
|
||||
expect' (P.run (P.Capture (P.NotCharIn "0123")) "a3b" == Just (["a"], "3b"))
|
||||
expect' (P.run (P.Many (P.CharIn "abcd")) "babbababac123" == Just ([], "123"))
|
||||
expect' (P.run (P.Capture (P.Many (P.CharIn "abcd"))) "babbababac123" == Just (["babbababac"], "123"))
|
||||
expect' (P.run (P.Capture (P.Many (P.Digit))) "012345abc" == Just (["012345"], "abc"))
|
||||
expect' (P.run (P.Join [P.Capture (P.Many (P.Digit)), P.Literal ",", P.Capture (P.Many P.AnyChar)]) "012345,abc" == Just (["012345", "abc"], ""))
|
||||
expect' (P.run (P.Join [P.Capture (P.Join [P.Capture (P.Char P.Any), P.Capture (P.Char P.Any)]), P.Char P.Any]) "abcdef" == Just (["ab"], "def"))
|
||||
expect' (P.run (P.Char (P.CharSet "0123")) "3ab" == Just ([], "ab"))
|
||||
expect' (P.run (P.Char (P.Not (P.CharSet "0123"))) "a3b" == Just ([], "3b"))
|
||||
expect' (P.run (P.Capture (P.Char (P.Not (P.CharSet "0123")))) "a3b" == Just (["a"], "3b"))
|
||||
expect' (P.run (P.Many (P.Char (P.CharSet "abcd"))) "babbababac123" == Just ([], "123"))
|
||||
expect' (P.run (P.Capture (P.Many (P.Char (P.CharSet "abcd")))) "babbababac123" == Just (["babbababac"], "123"))
|
||||
expect' (P.run (P.Capture (P.Many (P.Char (P.CharClass P.Number)))) "012345abc" == Just (["012345"], "abc"))
|
||||
expect' (P.run (P.Join [P.Capture (P.Many (P.Char (P.CharClass P.Number))), P.Literal ",", P.Capture (P.Many (P.Char P.Any))]) "012345,abc" == Just (["012345", "abc"], ""))
|
||||
expect'
|
||||
( P.run (P.Many (P.Join [P.Capture (P.Many (P.Digit)), P.Many P.Space])) "01 10 20 1123 292 110 10"
|
||||
( P.run (P.Many (P.Join [P.Capture (P.Many (P.Char (P.CharClass P.Number))), P.Many (P.Char (P.CharClass P.Whitespace))])) "01 10 20 1123 292 110 10"
|
||||
== Just (["01", "10", "20", "1123", "292", "110", "10"], "")
|
||||
)
|
||||
expect' $
|
||||
let part = P.Capture (P.Replicate 1 3 (P.Digit))
|
||||
let part = P.Capture (P.Replicate 1 3 (P.Char (P.CharClass P.Number)))
|
||||
dpart = P.Join [P.Literal ".", part]
|
||||
ip = P.Join [part, P.Replicate 3 3 dpart, P.Eof]
|
||||
in P.run ip "127.0.0.1" == Just (["127", "0", "0", "1"], "")
|
||||
expect' $
|
||||
let p = P.Replicate 5 8 (P.Capture P.Digit)
|
||||
let p = P.Replicate 5 8 (P.Capture (P.Char (P.CharClass P.Number)))
|
||||
in P.run p "12345" == Just (["1", "2", "3", "4", "5"], "")
|
||||
expect' $
|
||||
let p = P.Replicate 5 8 (P.Capture P.Digit) `P.Or` P.Join []
|
||||
let p = P.Replicate 5 8 (P.Capture (P.Char (P.CharClass P.Number))) `P.Or` P.Join []
|
||||
in P.run p "1234" == Just ([], "1234")
|
||||
expect' $
|
||||
let p = P.Replicate 5 8 (P.Capture (P.Join [P.Digit, P.Literal "z"])) `P.Or` P.Join []
|
||||
let p = P.Replicate 5 8 (P.Capture (P.Join [P.Char (P.CharClass P.Number), P.Literal "z"])) `P.Or` P.Join []
|
||||
in P.run p "1z2z3z4z5z6a" == Just (["1z", "2z", "3z", "4z", "5z"], "6a")
|
||||
-- https://github.com/unisonweb/unison/issues/3530
|
||||
expectEqual Nothing $
|
||||
@ -156,10 +157,10 @@ test =
|
||||
-- this is just making sure we don't duplicate captures to our left
|
||||
-- when entering an `Or` node
|
||||
expectEqual (Just (["@"], "")) $
|
||||
let p = P.Join [P.Capture P.AnyChar, P.Or (P.Literal "c") (P.Join []), P.Literal "d"]
|
||||
let p = P.Join [P.Capture (P.Char P.Any), P.Or (P.Literal "c") (P.Join []), P.Literal "d"]
|
||||
in P.run p "@cd"
|
||||
expectEqual (Just (["%", "c"], "")) $
|
||||
let p = P.Join [P.Capture P.AnyChar, (P.Or (P.Capture (P.Literal "c")) (P.Join [])), P.Literal "d"]
|
||||
let p = P.Join [P.Capture (P.Char P.Any), (P.Or (P.Capture (P.Literal "c")) (P.Join [])), P.Literal "d"]
|
||||
in P.run p "%cd"
|
||||
expectEqual (Just ([""], "ac")) $
|
||||
let p = P.Capture (P.Or (P.Join [P.Literal "a", P.Literal "b"]) (P.Join []))
|
||||
|
29
scheme-libs/chez/unison/concurrent.ss
Normal file
29
scheme-libs/chez/unison/concurrent.ss
Normal file
@ -0,0 +1,29 @@
|
||||
(library (unison concurrent)
|
||||
(export
|
||||
ref-new
|
||||
ref-read
|
||||
ref-write
|
||||
ref-cas
|
||||
promise-new
|
||||
promise-read
|
||||
promise-write
|
||||
promise-try-read
|
||||
fork
|
||||
kill
|
||||
sleep)
|
||||
|
||||
|
||||
(define err "This operation is not supported on the pure Chez Scheme
|
||||
backend, use the Racket over Chez Scheme backend")
|
||||
|
||||
;; TODO feels like there is a macro waiting to happen here
|
||||
(define (ref-new a) (error err))
|
||||
(define (ref-read ref) (error err))
|
||||
(define (ref-write ref a) (error err))
|
||||
(define (ref-cas ref old-value new-value) (error err))
|
||||
(define (promise-new) (error err))
|
||||
(define (promise-read promise) (error err))
|
||||
(define (promise-try-read promise) (error err))
|
||||
(define (fork thread-thunk) (error err))
|
||||
(define (kill thread-id) (error err)))
|
||||
|
63
scheme-libs/common/unison/data.ss
Normal file
63
scheme-libs/common/unison/data.ss
Normal file
@ -0,0 +1,63 @@
|
||||
;; Helpers for building data that conform to the compiler calling convention
|
||||
|
||||
#!r6rs
|
||||
(library (unison data)
|
||||
(export
|
||||
some
|
||||
none
|
||||
some?
|
||||
none?
|
||||
option-get
|
||||
right
|
||||
left
|
||||
right?
|
||||
left?
|
||||
either-get
|
||||
either-get
|
||||
unit
|
||||
false
|
||||
true)
|
||||
|
||||
(import (rnrs))
|
||||
|
||||
; Option a
|
||||
(define none `(0))
|
||||
|
||||
; a -> Option a
|
||||
(define (some a) `(1 ,a))
|
||||
|
||||
; Option a -> Bool
|
||||
(define (some? option) (eq? 1 (car option)))
|
||||
|
||||
; Option a -> Bool
|
||||
(define (none? option) (eq? 0 (car option)))
|
||||
|
||||
; Option a -> a (or #f)
|
||||
(define (option-get option)
|
||||
(if
|
||||
(some? option)
|
||||
(car (cdr option))
|
||||
(raise "Cannot get the value of an empty option ")))
|
||||
|
||||
; #<void> works as well
|
||||
; Unit
|
||||
(define unit `(0))
|
||||
|
||||
; Booleans are represented as numbers
|
||||
(define false 0)
|
||||
(define true 1)
|
||||
|
||||
; a -> Either b a
|
||||
(define (right a) `(1 ,a))
|
||||
|
||||
; b -> Either b a
|
||||
(define (left b) `(0 ,b))
|
||||
|
||||
; Either a b -> Boolean
|
||||
(define (right? either) (eq? 1 (car either)))
|
||||
|
||||
; Either a b -> Boolean
|
||||
(define (left? either) (eq? 0 (car either)))
|
||||
|
||||
; Either a b -> a | b
|
||||
(define (either-get either) (car (cdr either))))
|
@ -56,6 +56,23 @@
|
||||
|
||||
unison-FOp-Scope.bytearray
|
||||
unison-FOp-Scope.array
|
||||
unison-FOp-Scope.ref
|
||||
|
||||
unison-FOp-IO.ref
|
||||
unison-FOp-Ref.read
|
||||
unison-FOp-Ref.write
|
||||
unison-FOp-Ref.readForCas
|
||||
unison-FOp-Ref.Ticket.read
|
||||
unison-FOp-Ref.cas
|
||||
|
||||
unison-FOp-Promise.new
|
||||
unison-FOp-Promise.read
|
||||
unison-FOp-Promise.tryRead
|
||||
unison-FOp-Promise.write
|
||||
|
||||
unison-FOp-IO.delay.impl.v3
|
||||
unison-POp-FORK
|
||||
unison-FOp-IO.kill.impl.v3
|
||||
|
||||
unison-POp-ADDN
|
||||
unison-POp-ANDN
|
||||
@ -129,7 +146,8 @@
|
||||
(unison string)
|
||||
(unison crypto)
|
||||
(unison bytevector)
|
||||
(unison vector))
|
||||
(unison vector)
|
||||
(unison concurrent))
|
||||
|
||||
(define unison-POp-UPKB bytevector->u8-list)
|
||||
(define unison-POp-ADDI +)
|
||||
@ -308,5 +326,18 @@
|
||||
(define (unison-FOp-Scope.bytearray n) (make-bytevector n))
|
||||
(define (unison-FOp-Scope.array n) (make-vector n))
|
||||
|
||||
)
|
||||
(define (unison-POp-FORK thunk) (fork thunk))
|
||||
(define (unison-FOp-IO.delay.impl.v3 micros) (sleep micros))
|
||||
(define (unison-FOp-IO.kill.impl.v3 threadId) (kill threadId))
|
||||
(define (unison-FOp-Scope.ref a) (ref-new a))
|
||||
(define (unison-FOp-IO.ref a) (ref-new a))
|
||||
(define (unison-FOp-Ref.read ref) (ref-read ref))
|
||||
(define (unison-FOp-Ref.write ref a) (ref-write ref a))
|
||||
(define (unison-FOp-Ref.readForCas ref) (ref-read ref))
|
||||
(define (unison-FOp-Ref.Ticket.read ticket) ticket)
|
||||
(define (unison-FOp-Ref.cas ref ticket value) (ref-cas ref ticket value))
|
||||
(define (unison-FOp-Promise.new) (promise-new))
|
||||
(define (unison-FOp-Promise.read promise) (promise-read promise))
|
||||
(define (unison-FOp-Promise.tryRead promise) (promise-try-read promise))
|
||||
(define (unison-FOp-Promise.write promise a) (promise-write promise a)))
|
||||
|
||||
|
89
scheme-libs/racket/unison/concurrent.ss
Normal file
89
scheme-libs/racket/unison/concurrent.ss
Normal file
@ -0,0 +1,89 @@
|
||||
#!r6rs
|
||||
|
||||
(library (unison concurrent)
|
||||
(export
|
||||
ref-new
|
||||
ref-read
|
||||
ref-write
|
||||
ref-cas
|
||||
promise-new
|
||||
promise-read
|
||||
promise-write
|
||||
promise-try-read
|
||||
fork
|
||||
kill
|
||||
sleep)
|
||||
|
||||
(import (rnrs)
|
||||
(rnrs records syntactic)
|
||||
(unison data)
|
||||
(rename
|
||||
(only (racket base)
|
||||
box
|
||||
unbox
|
||||
set-box!
|
||||
box-cas!
|
||||
make-semaphore
|
||||
semaphore-peek-evt
|
||||
semaphore-post
|
||||
sync/enable-break
|
||||
thread
|
||||
break-thread
|
||||
parameterize-break
|
||||
sleep
|
||||
printf
|
||||
exn:break?
|
||||
with-handlers)
|
||||
(box ref-new)
|
||||
(unbox ref-read)
|
||||
(set-box! ref-write)
|
||||
(sleep sleep-secs))
|
||||
(only (racket unsafe ops) unsafe-struct*-cas!)
|
||||
(unison data))
|
||||
|
||||
(define-record-type promise (fields semaphore event (mutable value)))
|
||||
|
||||
(define (promise-new)
|
||||
(let* ([sem (make-semaphore)]
|
||||
[evt (semaphore-peek-evt sem)]
|
||||
[value none])
|
||||
(make-promise sem evt value)))
|
||||
|
||||
(define (promise-try-read promise) (promise-value promise))
|
||||
|
||||
(define (promise-read promise)
|
||||
(let loop ()
|
||||
(let ([value (promise-value promise)])
|
||||
(cond
|
||||
[(some? value) (option-get value)]
|
||||
[else (sync/enable-break (promise-event promise)) (loop)]))))
|
||||
|
||||
(define (promise-write promise new-value)
|
||||
(let loop ()
|
||||
(let* ([value (promise-value promise)]
|
||||
[cas! (lambda () (unsafe-struct*-cas! promise 2 value (some new-value)))]
|
||||
[awake-readers (lambda () (semaphore-post (promise-semaphore promise)))])
|
||||
(cond
|
||||
[(some? value) false]
|
||||
[else
|
||||
(let ([ok (parameterize-break #f (if (cas!) (awake-readers) false))])
|
||||
(if ok true (loop)))]))))
|
||||
|
||||
(define (ref-cas ref ticket value)
|
||||
(if (box-cas! ref ticket value) true false))
|
||||
|
||||
(define (sleep n)
|
||||
(sleep-secs (/ n 1000000))
|
||||
(right unit))
|
||||
|
||||
;; Swallows uncaught breaks/thread kills rather than logging them to
|
||||
;; match the behaviour of the Haskell runtime
|
||||
(define (fork thunk)
|
||||
(thread
|
||||
(lambda ()
|
||||
(with-handlers ([exn:break? (lambda (x) ())])
|
||||
(thunk)))))
|
||||
|
||||
(define (kill threadId)
|
||||
(break-thread threadId)
|
||||
(right unit)))
|
@ -36,8 +36,14 @@ import System.Directory
|
||||
getXdgDirectory,
|
||||
)
|
||||
import System.Environment (withArgs)
|
||||
import System.Exit (ExitCode (..))
|
||||
import System.FilePath ((</>))
|
||||
import System.Process (callProcess, readCreateProcess, shell)
|
||||
import System.Process
|
||||
( callProcess,
|
||||
readCreateProcess,
|
||||
readCreateProcessWithExitCode,
|
||||
shell,
|
||||
)
|
||||
import qualified Text.Megaparsec as P
|
||||
import qualified U.Codebase.Branch.Diff as V2Branch
|
||||
import qualified U.Codebase.Causal as V2Causal
|
||||
@ -2661,7 +2667,7 @@ doFetchCompiler =
|
||||
ns =
|
||||
ReadShareRemoteNamespace
|
||||
{ server = RemoteRepo.DefaultCodeserver,
|
||||
repo = ShareUserHandle "dolio",
|
||||
repo = ShareUserHandle "unison",
|
||||
path =
|
||||
Path.fromList $ NameSegment <$> ["public", "internal", "trunk"]
|
||||
}
|
||||
@ -2743,37 +2749,51 @@ typecheckAndEval ppe tm = do
|
||||
a = External
|
||||
rendered = P.toPlainUnbroken $ TP.pretty ppe tm
|
||||
|
||||
ensureSchemeExists :: Cli ()
|
||||
ensureSchemeExists =
|
||||
ensureSchemeExists :: SchemeBackend -> Cli ()
|
||||
ensureSchemeExists bk =
|
||||
liftIO callScheme >>= \case
|
||||
True -> pure ()
|
||||
False -> Cli.returnEarly (PrintMessage msg)
|
||||
where
|
||||
msg =
|
||||
P.lines
|
||||
[ "I can't seem to call scheme. See",
|
||||
"",
|
||||
P.indentN
|
||||
2
|
||||
"https://github.com/cisco/ChezScheme/blob/main/BUILDING",
|
||||
"",
|
||||
"for how to install Chez Scheme."
|
||||
]
|
||||
msg = case bk of
|
||||
Racket ->
|
||||
P.lines
|
||||
[ "I can't seem to call racket. See",
|
||||
"",
|
||||
P.indentN
|
||||
2
|
||||
"https://download.racket-lang.org/",
|
||||
"",
|
||||
"for how to install Racket."
|
||||
]
|
||||
Chez ->
|
||||
P.lines
|
||||
[ "I can't seem to call scheme. See",
|
||||
"",
|
||||
P.indentN
|
||||
2
|
||||
"https://github.com/cisco/ChezScheme/blob/main/BUILDING",
|
||||
"",
|
||||
"for how to install Chez Scheme."
|
||||
]
|
||||
|
||||
cmd = case bk of
|
||||
Racket -> "racket -l- raco help"
|
||||
Chez -> "scheme -q"
|
||||
callScheme =
|
||||
catch
|
||||
(True <$ readCreateProcess (shell "scheme -q") "")
|
||||
(\(_ :: IOException) -> pure False)
|
||||
readCreateProcessWithExitCode (shell cmd) "" >>= \case
|
||||
(ExitSuccess, _, _) -> pure True
|
||||
(ExitFailure _, _, _) -> pure False
|
||||
|
||||
racketOpts :: FilePath -> FilePath -> FilePath -> [String] -> [String]
|
||||
racketOpts gendir statdir file args = libs ++ [file] ++ args
|
||||
racketOpts :: FilePath -> FilePath -> [String] -> [String]
|
||||
racketOpts gendir statdir args = libs ++ args
|
||||
where
|
||||
includes = [gendir, statdir </> "common", statdir </> "racket"]
|
||||
libs = concatMap (\dir -> ["-S", dir]) includes
|
||||
|
||||
chezOpts :: FilePath -> FilePath -> FilePath -> [String] -> [String]
|
||||
chezOpts gendir statdir file args =
|
||||
"-q" : opt ++ libs ++ ["--script", file] ++ args
|
||||
chezOpts :: FilePath -> FilePath -> [String] -> [String]
|
||||
chezOpts gendir statdir args =
|
||||
"-q" : opt ++ libs ++ ["--script"] ++ args
|
||||
where
|
||||
includes = [gendir, statdir </> "common", statdir </> "chez"]
|
||||
libs = ["--libdirs", List.intercalate ":" includes]
|
||||
@ -2782,14 +2802,14 @@ chezOpts gendir statdir file args =
|
||||
data SchemeBackend = Racket | Chez
|
||||
|
||||
runScheme :: SchemeBackend -> String -> [String] -> Cli ()
|
||||
runScheme bk file args0 = do
|
||||
ensureSchemeExists
|
||||
runScheme bk file args = do
|
||||
ensureSchemeExists bk
|
||||
gendir <- getSchemeGenLibDir
|
||||
statdir <- getSchemeStaticLibDir
|
||||
let cmd = case bk of Racket -> "racket"; Chez -> "scheme"
|
||||
opts = case bk of
|
||||
Racket -> racketOpts gendir statdir file args0
|
||||
Chez -> chezOpts gendir statdir file args0
|
||||
Racket -> racketOpts gendir statdir (file : args)
|
||||
Chez -> chezOpts gendir statdir (file : args)
|
||||
success <-
|
||||
liftIO $
|
||||
(True <$ callProcess cmd opts)
|
||||
@ -2797,11 +2817,28 @@ runScheme bk file args0 = do
|
||||
unless success $
|
||||
Cli.returnEarly (PrintMessage "Scheme evaluation failed.")
|
||||
|
||||
buildChez :: String -> String -> Cli ()
|
||||
buildChez main file = do
|
||||
ensureSchemeExists
|
||||
buildScheme :: SchemeBackend -> String -> String -> Cli ()
|
||||
buildScheme bk main file = do
|
||||
ensureSchemeExists bk
|
||||
statDir <- getSchemeStaticLibDir
|
||||
genDir <- getSchemeGenLibDir
|
||||
build genDir statDir main file
|
||||
where
|
||||
build
|
||||
| Racket <- bk = buildRacket
|
||||
| Chez <- bk = buildChez
|
||||
|
||||
buildRacket :: String -> String -> String -> String -> Cli ()
|
||||
buildRacket genDir statDir main file =
|
||||
let args = ["-l", "raco", "--", "exe", "-o", main, file]
|
||||
opts = racketOpts genDir statDir args
|
||||
in void . liftIO $
|
||||
catch
|
||||
(True <$ callProcess "racket" opts)
|
||||
(\(_ :: IOException) -> pure False)
|
||||
|
||||
buildChez :: String -> String -> String -> String -> Cli ()
|
||||
buildChez genDir statDir main file = do
|
||||
let cmd = shell "scheme -q --optimize-level 3"
|
||||
void . liftIO $ readCreateProcess cmd (build statDir genDir)
|
||||
where
|
||||
@ -2830,7 +2867,7 @@ doRunAsScheme main args = do
|
||||
|
||||
doCompileScheme :: String -> HQ.HashQualified Name -> Cli ()
|
||||
doCompileScheme out main =
|
||||
generateSchemeFile False out main >>= buildChez out
|
||||
generateSchemeFile True out main >>= buildScheme Racket out
|
||||
|
||||
generateSchemeFile :: Bool -> String -> HQ.HashQualified Name -> Cli String
|
||||
generateSchemeFile exec out main = do
|
||||
|
@ -293,6 +293,9 @@ stmRef = Reference.Builtin "STM"
|
||||
patternRef :: Reference
|
||||
patternRef = Reference.Builtin "Pattern"
|
||||
|
||||
charClassRef :: Reference
|
||||
charClassRef = Reference.Builtin "Char.Class"
|
||||
|
||||
tlsClientConfigRef :: Reference
|
||||
tlsClientConfigRef = Reference.Builtin "Tls.ClientConfig"
|
||||
|
||||
@ -641,7 +644,7 @@ removePureEffects :: (ABT.Var v) => Type v a -> Type v a
|
||||
removePureEffects t
|
||||
| not Settings.removePureEffects = t
|
||||
| otherwise =
|
||||
generalize vs $ removeEffectVars fvs tu
|
||||
generalize vs $ removeEffectVars fvs tu
|
||||
where
|
||||
(vs, tu) = unforall' t
|
||||
vss = Set.fromList vs
|
||||
|
127
unison-src/builtin-tests/concurrency-tests.u
Normal file
127
unison-src/builtin-tests/concurrency-tests.u
Normal file
@ -0,0 +1,127 @@
|
||||
concurrency.tests = Tests.main do
|
||||
!simpleRefTest
|
||||
!simpleRefTestScope
|
||||
!ticketTest
|
||||
!casTest
|
||||
!promiseSequentialTest
|
||||
!promiseConcurrentTest
|
||||
!forkKillTest
|
||||
-- !tryEvalForkTest
|
||||
!fullTest
|
||||
|
||||
simpleRefTest = do
|
||||
r = IO.ref 0
|
||||
Ref.write r 1
|
||||
i = Ref.read r
|
||||
Ref.write r 2
|
||||
j = Ref.read r
|
||||
Ref.write r 5
|
||||
checkEqual "Ref read-write" (i, j, Ref.read r) (1, 2, 5)
|
||||
|
||||
simpleRefTestScope = do
|
||||
Scope.run do
|
||||
r = Scope.ref 0
|
||||
Ref.write r 1
|
||||
i = Ref.read r
|
||||
Ref.write r 2
|
||||
j = Ref.read r
|
||||
Ref.write r 5
|
||||
checkEqual "Ref read-write" (i, j, Ref.read r) (1, 2, 5)
|
||||
|
||||
ticketTest = do
|
||||
r = IO.ref 3
|
||||
t = Ref.readForCas r
|
||||
v = Ticket.read t
|
||||
checkEqual "Ticket contains the Ref value" v 3
|
||||
|
||||
casTest = do
|
||||
ref = IO.ref 0
|
||||
ticket = Ref.readForCas ref
|
||||
v1 = Ref.cas ref ticket 5
|
||||
check "CAS is successful is there were no conflicting writes" 'v1
|
||||
Ref.write ref 10
|
||||
v2 = Ref.cas ref ticket 15
|
||||
check "CAS fails when there was an intervening write" '(not v2)
|
||||
|
||||
promiseSequentialTest = do
|
||||
use Nat eq
|
||||
use Promise read write
|
||||
p = !Promise.new
|
||||
v0 = Promise.tryRead p
|
||||
checkEqual "Promise should be empty when created" v0 None
|
||||
Promise.write_ p 0
|
||||
v1 = read p
|
||||
checkEqual "Promise should read a value that's been written" v1 0
|
||||
Promise.write_ p 1
|
||||
v2 = read p
|
||||
checkEqual "Promise can only be written to once" v2 v1
|
||||
v3 = Promise.tryRead p
|
||||
checkEqual "Once the Promise is full, tryRead is the same as read" v3 (Some v2)
|
||||
|
||||
millis = 1000
|
||||
sleep_ n = unsafeRun! do sleep n
|
||||
|
||||
promiseConcurrentTest = do
|
||||
use Nat eq
|
||||
use concurrent fork
|
||||
p = !Promise.new
|
||||
_ = fork do
|
||||
sleep_ (200 * millis)
|
||||
Promise.write p 5
|
||||
v = Promise.read p
|
||||
checkEqual "Reads awaits for completion of the Promise" v 5
|
||||
|
||||
kill_ t = unsafeRun! do concurrent.kill t
|
||||
|
||||
forkKillTest = do
|
||||
ref = IO.ref "initial"
|
||||
thread = fork do
|
||||
sleep_ (400 * millis)
|
||||
Ref.write ref "done"
|
||||
sleep_ (200 * millis)
|
||||
kill_ thread
|
||||
sleep_ (300 * millis)
|
||||
v = Ref.read ref
|
||||
checkEqual "Thread was killed" v "initial"
|
||||
|
||||
tryEvalForkTest = bug "Depends on the Exception ability being implemented"
|
||||
|
||||
atomicUpdate : Ref {IO} a -> (a -> a) ->{IO} ()
|
||||
atomicUpdate ref f =
|
||||
ticket = Ref.readForCas ref
|
||||
value = f (Ticket.read ticket)
|
||||
if Ref.cas ref ticket value then () else atomicUpdate ref f
|
||||
|
||||
spawnN : Nat -> '{IO} a ->{IO} [a]
|
||||
spawnN n fa =
|
||||
use Nat eq -
|
||||
use concurrent fork
|
||||
|
||||
go i acc =
|
||||
if eq i 0
|
||||
then acc
|
||||
else
|
||||
value = !Promise.new
|
||||
_ = fork do Promise.write value !fa
|
||||
go (i - 1) (acc :+ value)
|
||||
|
||||
map Promise.read (go n [])
|
||||
|
||||
fullTest = do
|
||||
use Nat * + eq -
|
||||
|
||||
numThreads = 100
|
||||
iterations = 100
|
||||
expected = numThreads * iterations
|
||||
|
||||
state = IO.ref 0
|
||||
thread n =
|
||||
if eq n 0
|
||||
then ()
|
||||
else
|
||||
atomicUpdate state (v -> v + 1)
|
||||
thread (n - 1)
|
||||
ignore (spawnN numThreads '(thread iterations))
|
||||
result = Ref.read state
|
||||
checkEqual "The state of the counter is consistent " result expected
|
||||
|
@ -17,4 +17,14 @@ to `Tests.check` and `Tests.checkEqual`).
|
||||
|
||||
```ucm
|
||||
.> run tests
|
||||
```
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> load unison-src/builtin-tests/concurrency-tests.u
|
||||
.> add
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> run concurrency.tests
|
||||
```
|
||||
|
||||
|
@ -11,3 +11,9 @@ to `Tests.check` and `Tests.checkEqual`).
|
||||
()
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> run concurrency.tests
|
||||
|
||||
()
|
||||
|
||||
```
|
||||
|
@ -19,4 +19,13 @@ to `Tests.check` and `Tests.checkEqual`).
|
||||
|
||||
```ucm
|
||||
.> run.native tests
|
||||
```
|
||||
```
|
||||
|
||||
```ucm:hide
|
||||
.> load unison-src/builtin-tests/concurrency-tests.u
|
||||
.> add
|
||||
```
|
||||
|
||||
```ucm
|
||||
.> run.native concurrency.tests
|
||||
```
|
||||
|
@ -9,3 +9,7 @@ to `Tests.check` and `Tests.checkEqual`).
|
||||
.> run.native tests
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> run.native concurrency.tests
|
||||
|
||||
```
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,7 @@ The `builtins.merge` command adds the known builtins to a `builtin` subnamespace
|
||||
5. Bytes (builtin type)
|
||||
6. Bytes/ (33 terms)
|
||||
7. Char (builtin type)
|
||||
8. Char/ (3 terms)
|
||||
8. Char/ (22 terms, 1 type)
|
||||
9. Code (builtin type)
|
||||
10. Code/ (8 terms)
|
||||
11. Debug/ (3 terms)
|
||||
@ -63,7 +63,7 @@ The `builtins.merge` command adds the known builtins to a `builtin` subnamespace
|
||||
52. Socket/ (1 term)
|
||||
53. Test/ (2 terms, 1 type)
|
||||
54. Text (builtin type)
|
||||
55. Text/ (32 terms)
|
||||
55. Text/ (33 terms)
|
||||
56. ThreadId/ (1 term)
|
||||
57. Tuple (type)
|
||||
58. Tuple/ (1 term)
|
||||
|
@ -23,7 +23,7 @@ Technically, the definitions all exist, but they have no names. `builtins.merge`
|
||||
|
||||
.foo> ls
|
||||
|
||||
1. builtin/ (420 terms, 64 types)
|
||||
1. builtin/ (440 terms, 65 types)
|
||||
|
||||
```
|
||||
And for a limited time, you can get even more builtin goodies:
|
||||
@ -35,7 +35,7 @@ And for a limited time, you can get even more builtin goodies:
|
||||
|
||||
.foo> ls
|
||||
|
||||
1. builtin/ (592 terms, 82 types)
|
||||
1. builtin/ (612 terms, 83 types)
|
||||
|
||||
```
|
||||
More typically, you'd start out by pulling `base.
|
||||
|
@ -7,6 +7,8 @@ Tests that functions named `.` are rendered correctly.
|
||||
``` unison
|
||||
(.) f g x = f (g x)
|
||||
|
||||
use Boolean not
|
||||
|
||||
noop = not . not
|
||||
```
|
||||
|
||||
|
@ -3,6 +3,8 @@ Tests that functions named `.` are rendered correctly.
|
||||
```unison
|
||||
(.) f g x = f (g x)
|
||||
|
||||
use Boolean not
|
||||
|
||||
noop = not . not
|
||||
```
|
||||
|
||||
@ -31,6 +33,8 @@ noop = not . not
|
||||
.> view noop
|
||||
|
||||
noop : Boolean -> Boolean
|
||||
noop = not . not
|
||||
noop =
|
||||
use Boolean not
|
||||
not . not
|
||||
|
||||
```
|
||||
|
@ -113,13 +113,13 @@ it's still in the `history` of the parent namespace and can be resurrected at an
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #492bge1qkb
|
||||
⊙ 1. #pfspc3m714
|
||||
|
||||
- Deletes:
|
||||
|
||||
feature1.y
|
||||
|
||||
⊙ 2. #qdsgea37fc
|
||||
⊙ 2. #t9gdv3652e
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
@ -130,26 +130,26 @@ it's still in the `history` of the parent namespace and can be resurrected at an
|
||||
Original name New name(s)
|
||||
feature1.y master.y
|
||||
|
||||
⊙ 3. #ppkkh269f7
|
||||
⊙ 3. #fnah4umom7
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
feature1.y
|
||||
|
||||
⊙ 4. #u8aiheqfug
|
||||
⊙ 4. #fsd9t403lp
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
x master.x
|
||||
|
||||
⊙ 5. #es9cmc7kok
|
||||
⊙ 5. #64tba28sdf
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
x
|
||||
|
||||
□ 6. #jo7t8m4dft (start of history)
|
||||
□ 6. #uql7vkh78v (start of history)
|
||||
|
||||
```
|
||||
To resurrect an old version of a namespace, you can learn its hash via the `history` command, then use `fork #namespacehash .newname`.
|
||||
|
@ -269,7 +269,7 @@ I should be able to move the root into a sub-namespace
|
||||
|
||||
.> ls
|
||||
|
||||
1. root/ (597 terms, 83 types)
|
||||
1. root/ (617 terms, 84 types)
|
||||
|
||||
.> history
|
||||
|
||||
@ -278,13 +278,13 @@ I should be able to move the root into a sub-namespace
|
||||
|
||||
|
||||
|
||||
□ 1. #bn675bbtpm (start of history)
|
||||
□ 1. #g5nn5l3b03 (start of history)
|
||||
|
||||
```
|
||||
```ucm
|
||||
.> ls .root.at.path
|
||||
|
||||
1. builtin/ (592 terms, 82 types)
|
||||
1. builtin/ (612 terms, 83 types)
|
||||
2. existing/ (1 term)
|
||||
3. happy/ (3 terms, 1 type)
|
||||
4. history/ (1 term)
|
||||
@ -294,7 +294,7 @@ I should be able to move the root into a sub-namespace
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #vor04lbt72
|
||||
⊙ 1. #vt3jsa8k80
|
||||
|
||||
- Deletes:
|
||||
|
||||
@ -305,7 +305,7 @@ I should be able to move the root into a sub-namespace
|
||||
Original name New name
|
||||
existing.a.termInA existing.b.termInA
|
||||
|
||||
⊙ 2. #tk3qtdeoov
|
||||
⊙ 2. #b2h3s5rv29
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
@ -317,26 +317,26 @@ I should be able to move the root into a sub-namespace
|
||||
happy.b.termInA existing.a.termInA
|
||||
history.b.termInA existing.a.termInA
|
||||
|
||||
⊙ 3. #r971i7m95i
|
||||
⊙ 3. #7v6bvecsm0
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
existing.a.termInA existing.b.termInB
|
||||
|
||||
⊙ 4. #6qh988adub
|
||||
⊙ 4. #1uf0leagkk
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
history.a.termInA history.b.termInA
|
||||
|
||||
⊙ 5. #g19mlrid0i
|
||||
⊙ 5. #a3uao3fp6q
|
||||
|
||||
- Deletes:
|
||||
|
||||
history.b.termInB
|
||||
|
||||
⊙ 6. #n0a5seofan
|
||||
⊙ 6. #umd1mp6mku
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
@ -347,13 +347,13 @@ I should be able to move the root into a sub-namespace
|
||||
Original name New name(s)
|
||||
happy.b.termInA history.a.termInA
|
||||
|
||||
⊙ 7. #i3nsbtl7kc
|
||||
⊙ 7. #dqfd14almm
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
history.a.termInA history.b.termInB
|
||||
|
||||
⊙ 8. #a2u0kep087
|
||||
⊙ 8. #ljk3oa07ld
|
||||
|
||||
> Moves:
|
||||
|
||||
@ -363,7 +363,7 @@ I should be able to move the root into a sub-namespace
|
||||
happy.a.T.T2 happy.b.T.T2
|
||||
happy.a.termInA happy.b.termInA
|
||||
|
||||
⊙ 9. #g18uf760mb
|
||||
⊙ 9. #hhun973gp5
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
@ -373,7 +373,7 @@ I should be able to move the root into a sub-namespace
|
||||
|
||||
happy.a.T.T
|
||||
|
||||
⊙ 10. #2edl4803r1
|
||||
⊙ 10. #8ri4h5gjvo
|
||||
|
||||
+ Adds / updates:
|
||||
|
||||
@ -385,7 +385,7 @@ I should be able to move the root into a sub-namespace
|
||||
|
||||
⠇
|
||||
|
||||
⊙ 11. #qcd5obbuv8
|
||||
⊙ 11. #ahcsbbqt21
|
||||
|
||||
|
||||
```
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -59,17 +59,17 @@ y = 2
|
||||
most recent, along with the command that got us there. Try:
|
||||
|
||||
`fork 2 .old`
|
||||
`fork #2b8npf0tu2 .old` to make an old namespace
|
||||
`fork #s4kjl4lbf3 .old` to make an old namespace
|
||||
accessible again,
|
||||
|
||||
`reset-root #2b8npf0tu2` to reset the root namespace and
|
||||
`reset-root #s4kjl4lbf3` to reset the root namespace and
|
||||
its history to that of the
|
||||
specified namespace.
|
||||
|
||||
When Root Hash Action
|
||||
1. now #j967usn5hk add
|
||||
2. now #2b8npf0tu2 add
|
||||
3. now #lv9og66mct builtins.merge
|
||||
1. now #hr821c0ji5 add
|
||||
2. now #s4kjl4lbf3 add
|
||||
3. now #92606li9fc builtins.merge
|
||||
4. #sg60bvjo91 history starts here
|
||||
|
||||
Tip: Use `diff.namespace 1 7` to compare namespaces between
|
||||
|
@ -13,7 +13,7 @@ Let's look at some examples. We'll start with a namespace with just the builtins
|
||||
|
||||
|
||||
|
||||
□ 1. #a2uij441jg (start of history)
|
||||
□ 1. #3jjj6quqhh (start of history)
|
||||
|
||||
.> fork builtin builtin2
|
||||
|
||||
@ -42,21 +42,21 @@ Now suppose we `fork` a copy of builtin, then rename `Nat.+` to `frobnicate`, th
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #2orc0vqqcv
|
||||
⊙ 1. #6qm36657l4
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
Nat.frobnicate Nat.+
|
||||
|
||||
⊙ 2. #fk0nmiqqgk
|
||||
⊙ 2. #72ip8q9i3l
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
Nat.+ Nat.frobnicate
|
||||
|
||||
□ 3. #a2uij441jg (start of history)
|
||||
□ 3. #3jjj6quqhh (start of history)
|
||||
|
||||
```
|
||||
If we merge that back into `builtin`, we get that same chain of history:
|
||||
@ -71,21 +71,21 @@ If we merge that back into `builtin`, we get that same chain of history:
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #2orc0vqqcv
|
||||
⊙ 1. #6qm36657l4
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
Nat.frobnicate Nat.+
|
||||
|
||||
⊙ 2. #fk0nmiqqgk
|
||||
⊙ 2. #72ip8q9i3l
|
||||
|
||||
> Moves:
|
||||
|
||||
Original name New name
|
||||
Nat.+ Nat.frobnicate
|
||||
|
||||
□ 3. #a2uij441jg (start of history)
|
||||
□ 3. #3jjj6quqhh (start of history)
|
||||
|
||||
```
|
||||
Let's try again, but using a `merge.squash` (or just `squash`) instead. The history will be unchanged:
|
||||
@ -106,7 +106,7 @@ Let's try again, but using a `merge.squash` (or just `squash`) instead. The hist
|
||||
|
||||
|
||||
|
||||
□ 1. #a2uij441jg (start of history)
|
||||
□ 1. #3jjj6quqhh (start of history)
|
||||
|
||||
```
|
||||
The churn that happened in `mybuiltin` namespace ended up back in the same spot, so the squash merge of that namespace with our original namespace had no effect.
|
||||
@ -485,13 +485,13 @@ This checks to see that squashing correctly preserves deletions:
|
||||
Note: The most recent namespace hash is immediately below this
|
||||
message.
|
||||
|
||||
⊙ 1. #9ijnd9ip7o
|
||||
⊙ 1. #p9ur8e0jlu
|
||||
|
||||
- Deletes:
|
||||
|
||||
Nat.* Nat.+
|
||||
|
||||
□ 2. #a2uij441jg (start of history)
|
||||
□ 2. #3jjj6quqhh (start of history)
|
||||
|
||||
```
|
||||
Notice that `Nat.+` and `Nat.*` are deleted by the squash, and we see them deleted in one atomic step in the history.
|
||||
|
Loading…
Reference in New Issue
Block a user