Commit d8338e3e authored by Marc Coiffier's avatar Marc Coiffier
Browse files

Integrate the 'bashcomps.shl' library into the Curly package, so that...

Integrate the 'bashcomps.shl' library into the Curly package, so that unprivileged users can still benefit from great completions
parent 93d43de5
......@@ -95,8 +95,11 @@ evalDocWithPatterns pats = eval
eval' (Join (DocTag "or" [] xs)) = foldMap eval' xs
eval' (Join (DocTag "when" [] [x,y])) = eval' x >> eval' y
eval' (Join (DocTag "unless" [] [x,y])) = maybe (Just ()) (const Nothing) (eval' x) >> eval' y
eval' (Join (DocTag "splice" as xs)) = Join . DocTag "splice" as . foldr merge [] <$> traverse eval' xs
where merge x [] = [x]
eval' (Join (DocTag "splice" as xs)) = constr . foldr merge [] <$> traverse eval' xs
where constr [] = Pure ""
constr [x] = x
constr l = Join $ DocTag "splice" as l
merge x [] = [x]
merge (Pure x) (Pure y:t) = Pure (x+y):t
merge x t = x:t
eval' (Join (DocTag op [] [ea,eb]))
......
......@@ -2,7 +2,7 @@
-- see http://haskell.org/cabal/users-guide/
name: curly
version: 0.59.4.3
version: 0.59.4.5
synopsis: A minimal cross-compiler for the simply-typed lambda-calculus
-- description:
license: GPL-3
......@@ -22,6 +22,7 @@ data-files:
applications/curly-library.desktop
applications/curly-source.desktop
applications/curly-uri.desktop
bash/completions/bashcomps.shl
bash/completions/curly
bash/completions/curly.arg.shf
bash/completions/curly.script.shf
......@@ -34,6 +35,7 @@ data-files:
emacs/curly-utils.el
freeze.sh
install.sh
env.sh
kate/highlight-curly.xml
list
make/curly.mk
......
......@@ -3,9 +3,9 @@ declare -gA BASHCOMPS
declare -gA COMP_DESCRIPTIONS
declare -gA INTERPRETER_COMPS
if [ -z "$INCLUDED_BASHCOMPS" ]; then
INCLUDED_BASHCOMPS=true
INCLUDED_BASHCOMPS="$BASH_SOURCE"
declare -gix CI_PREFIX=0 CI_REST=1 CI_POP=2
function C.testBashCorrectness() {
# Some versions of Bash give incorrect results when splicing arrays
# that contain empty strings, so we need to check if we are running
......@@ -317,46 +317,33 @@ if [ -z "$INCLUDED_BASHCOMPS" ]; then
}
function C.fileIn() { C.argument word C.isFileIn "$@"; }
function C.isHostname() {
if C.leaf; then
SUGGESTIONS=( $(compgen -A hostname "$1") )
SUGGESTIONS+=( $(grep "^$1" "/usr/share/bash/bashcomps.d/common-hosts" 2> /dev/null | grep -F "$1" || :) )
else
SUGGESTIONS=( "$1" )
fi
shift
C.return "$@"
}
function C.hostname() { C.argument word C.isHostname "$@"; }
function C.isAny() { if [ -n "$1" ]; then SUGGESTIONS=( "$1" ); fi; C.return "${@:2}"; }
function C.any() { C.argument word C.isAny "$@"; }
function C.url() {
C.suffixed '://' C.wordOf 5 http https git ftp ssh \
C.suffixed "/" C.hostname \
C.normal C.any "$@"
}
function C.init() {
local name fname cmdname comp
for comp in /usr/share/bash/completions/*; do
if [ -r "$comp" ]; then
name="$(realpath "$comp")"
name="${name#/usr/share/bash/completions/}"
fname="$name"
case "$name" in
*.shf) fname="$name"; name="${name%.shf}";;
*.sh) . "$comp" ; continue;;
*)
cmdname="${comp#/usr/share/bash/completions/}"
fname="$name"
C.defcomp "C.$name" "$cmdname"
;;
esac
eval "function C.$name() { source /usr/share/bash/completions/$fname; }"
fi
done
local dir name fname cmdname comp ifs="$IFS"
local -a compdirs=( )
IFS=: compdirs+=( ${XDG_DATA_HOME:-"$HOME/.local/share"} ${XDG_DATA_DIRS:-/usr/local/share:/usr/share} ) IFS="$ifs"
for dir in "${compdirs[@]/%//bashcomps/completions}"; do
for comp in "$dir"/*; do
if [ -r "$comp" ]; then
name="$(realpath "$comp")"
name="${name#$dir/}"
fname="$name"
case "$name" in
*.shf) fname="$name"; name="${name%.shf}";;
*.sh) . "$comp" ; continue;;
*)
cmdname="${comp#$dir/}"
fname="$name"
C.defcomp "C.$name" "$cmdname"
;;
esac
eval "function C.$name() { source $dir/$fname; }"
fi
done
done
function C.default() {
local program="$(which "$1" 2>/dev/null || echo "$1")"
......
......@@ -10,8 +10,8 @@ function C.curly.init() {
COMP_DESCRIPTIONS["curly:$short"]="$desc"; fi
if [ "${long:+x}" = x ]; then
COMP_DESCRIPTIONS["curly:$long"]="$desc"; fi
done < <(CURLY_VCS=dummy /usr/bin/curly -h | tail -n +2 | sed -rn 's/^\s+(-[^-])?\s+(--\S+)\s+(\S+)\s+(.*\S)\s*$/\1|\2|\3|\4/p')
IFS=', ' CURLY_SYSTEMS=( $(CURLY_VCS=dummy /usr/bin/curly -h | sed -n 's/^Known systems: //p') ) IFS="$IFSBAK"
done < <(CURLY_VCS=dummy curly -h | tail -n +2 | sed -rn 's/^\s+(-[^-])?\s+(--\S+)\s+(\S+)\s+(.*\S)\s*$/\1|\2|\3|\4/p')
IFS=', ' CURLY_SYSTEMS=( $(CURLY_VCS=dummy curly -h | sed -n 's/^Known systems: //p') ) IFS="$IFSBAK"
}
function C.curly.flags() {
C.curly.init
......@@ -64,12 +64,27 @@ function C.curly.path() {
C.alt C.suffixed ":" C.argument word C.curly.isArg C.curly.path "$@"
C.alt C.suffixed "=" C.argument word C.curly.isArg "$@"
}
function C.curly.isPackageName() {
local arg="$1"; shift
SUGGESTIONS=( )
if C.leaf; then
while read _ name version descr; do
SUGGESTIONS+=( "$name -- Package: $descr (version $version)" )
done < <(PREFIX="$arg" TERM= curly %'repository list "{matches {$ name} "{env PREFIX}*"} {$ version} {$ synopsis}"')
fi
if ((${#SUGGESTIONS[@]} == 0)); then
SUGGESTIONS=( "$arg -- Unknown package" )
fi
C.returnRaw "$@"
}
function C.curly.packageName() { C.argument word C.curly.isPackageName "$@"; }
function C.curly.inputarg() {
C.alt C.suffixed ":" \
C.wordOf 1 source \
C.fileIn -d . \
C.suffixed "" C.fileIn -d . "$@"
C.alt C.suffixed ":" C.wordOf 1 library C.normal C.curly.hash "$@"
C.alt C.suffixed ":" C.wordOf 1 package C.normal C.curly.packageName "$@"
C.alt C.suffixed "" C.wordOf 1 builtins "$@"
}
function C.curly.input() { C.curly.path C.curly.inputarg "$@"; }
......@@ -92,13 +107,16 @@ function C.curly.library() {
}
function C.curly.isHash() {
local arg="$1" ; shift
: ${CURLY_LIBCACHE:=$HOME/.curly/libraries}
: ${CURLY_LIBCACHE:=$HOME/.cache/curly/libraries}
if C.leaf; then
IFSBAK="$IFS" IFS=$'\n' \
SUGGESTIONS=( $({ /usr/bin/curly -l'l"{$ synopsis}{or " (v{$ version})" ""}"'
for i in $(ls "$CURLY_LIBCACHE" 2> /dev/null); do
hash="${i%.cyl}"; head="$(head -1 "$CURLY_LIBCACHE/$i")"
echo "${head/##!*!#/$hash}"
SUGGESTIONS=( $({ TERM= curly %'repository list "{$ synopsis}{or " (v{$ version})" ""}"'
shopt -s nullglob
for i in "$CURLY_LIBCACHE"/*/*.cyl; do
head="$(head -1 "$i")"
hash="${i#$CURLY_LIBCACHE/}"
hash="${hash%.cyl}"
echo "${head/##!*!#/${hash//\//}}"
done; } \
| sort -u | sed -rn "s/^($arg\\S+)\\s*(.*)\$/\\1 -- \\2/p") ) IFS="$IFSBAK"
else
......@@ -107,17 +125,26 @@ function C.curly.isHash() {
C.returnRaw "$@"
}
function C.curly.hash() { C.argument word C.curly.isHash "$@"; }
function C.curly.goody() {
local -a goodies=( $(curly --goody list) )
C.wordOf "${#goodies[@]}" "${goodies[@]}" "$@"
}
function C.curly.withScript() {
local -a CURLY_COMP_SCRIPTS=( "${CURLY_COMP_SCRIPTS[@]}" "$SCRIPT" )
"$@"
}
function C.curly.instance() {
local -a instances=( $(cy @"$HOST"/_ -li 2> /dev/null | sed -n '/^Couldn.t connect to/!{s/^[^:]*:\s*//;y/,/ /;p}') )
local -a instances=( $(curly @"$HOST"/_ -l 2> /dev/null | sed -n '/^Couldn.t connect to/!{s/^[^:]*:\s*//;y/,/ /;p}') )
C.wordOf "${#instances[@]}" "${instances[@]}" "$@"
}
function C.curly.isHostname() {
local arg="$1"; shift
SUGGESTIONS=( "$arg -- Instance server" )
C.returnRaw "$@"
}
function C.curly.server() {
C.suffixed / C.capture word HOST C.hostname \
C.suffixed / C.capture word HOST C.argument word C.curly.isHostname \
C.suffixed "" C.curly.instance "$@"
}
function C.curly.run_arg() {
......@@ -143,18 +170,19 @@ function C.curly.arg() {
for ((i=0;i<${#compFlags[@]};i++)); do
compFlags[i]+=" -- ${compFlagDescs[${compFlags[i]}]}"
done
} < <(CURLY_VCS=dummy /usr/bin/curly "${CURLY_COMP_SCRIPTS[@]}" -h)
} < <(CURLY_VCS=dummy curly "${CURLY_COMP_SCRIPTS[@]}" -h)
C.alt C.rawWordOf "${#compFlags[@]}" "${compFlags[@]}" "$@"
fi
C.alt C.curly.flags flag 6 -h --help -v --version -i --interactive "$@"
C.alt C.curly.flags opt 4 -l --list -s --serve C.wordOf 2 libraries instances "$@"
C.alt C.curly.flags opt 4 -l --list-instances -s --serve-instance "$@"
C.alt C.curly.flags opt 2 -r --run C.curly.run_arg "$@"
C.alt C.curly.flags opt 2 -M --mount C.curly.input "$@"
C.alt C.curly.flags opt 2 -d --dump C.curly.library "$@"
C.alt C.curly.flags opt 2 -t --translate C.curly.translate "$@"
C.alt C.curly.flags opt 1 --banner C.fileIn -r . "$@"
C.alt C.curly.flags opt 7 -p -P --prelude --prelude+ --instance -e --execute C.any "$@"
C.alt C.curly.flags opt 1 --banner C.any "$@"
C.alt C.curly.flags opt 1 --goody C.curly.goody "$@"
C.alt C.curly.flags opt 8 -p -P --prelude --prelude+ --banner --banner+ --instance -e --execute C.any "$@"
C.alt C.curly.flags opt 1 --at C.curly.server "$@"
C.alt C.describing "Small mount description" C.suffixed ":" C.wordOf 1 package C.normal C.packageName "$@"
C.alt C.describing "Curly script, source or library" C.capture word SCRIPT C.fileIn CF.cyscript . C.curly.withScript C.describing "" "$@"
C.alt C.suffixed "@" C.wordOf 1 '' C.curly.server "$@"
}
......
#!/bin/bash
C.defcomp.default C.curly.script /usr/bin/curly
C.defcomp.default C.curly.script "/usr/bin/env curly"
#!/bin/sh
get_data() {
printf "Installing goody %s at location %s\n" "$2" "$1"
curly --goody="$2" > "$1"
}
case "$1" in
......@@ -22,4 +23,16 @@ EOF
rm -r "curly-$ver"
fi
;;
bash-completions)
root="${XDG_DATA_HOME:-$HOME/.local/share}/bashcomps"
mkdir -p "$root/completions"
if [ ! -e "$root/bashcomps.shl" ]; then
get_data "$root/bashcomps.shl" bash/completions/bashcomps.shl
fi
get_data "$root/completions/curly" bash/completions/curly
get_data "$root/completions/curly.arg.shf" bash/completions/curly.arg.shf
get_data "$root/completions/curly.script.shf" bash/completions/curly.script.shf
get_data "$root/completions/curly.sh" bash/completions/curly.sh
;;
esac
......@@ -43,6 +43,15 @@ associating the symbols `_+_`{.curly}, `_-_`{.curly}, `_*_`{.curly}
and `_/_`{.curly} to their corresponding function. The "square"
function can be abbreviated to `_²`{.curly} in the same manner.
~~~~{.curly}
# Example : arithmetic operators
define _+_ = addInt
define _-_ = subInt
define _*_ = mulInt
define _/_ = divInt
define _² x = x*x
~~~~~~~
In some cases, there can be ambiguity in the syntax. For example, the
expression `x+y*z`{.curly} can be interpreted as either
`(x+y)*z`{.curly} or `x+(y*z)`{.curly}. In those cases, the operators
......@@ -75,6 +84,36 @@ For instance, `x+_*y`{.curly} is equivalent to `{z: x+z*y}`{.curly},
and `if _ then true else _`{.curly} is equivalent to `{x y: if x then
true else y}`{.curly}.
### Local definitions in argument lists
We've now seen that functions can accept abstract arguments in the
form of variables. If we want to give a name to the value `(1+2)` in
the expression `(1+2)*((1+2)+3)` we can do so by writing the
equivalent expression `{x: x*(x+3)} (1+2)`.
This is a functionally correct way to factor out a value, but it's not
a very practical one for two reasons :
- the variable name can be syntactically far away from its value,
forcing the reader to jump back and forth in the code to find out its flow
- when dealing with multiple nested patterns, a lot of brackets become necessary
To avoid both those problems, Curly allows the previous expression to
be rewritten as `{{x = 1+2}: x*(x+3)}`. More generally, the special
form `{var arg... = body}` can appear anywhere in an argument list,
and has the effect of declaring a local variable `var` with a value of
`{arg...: body}`.
A local definition can refer to any parameter that precedes it,
including other local definitions. For example, the expression `{x {x2
= x*x} y {xy2 = x2+y*y}: sqrt xy2}` describes a function of two
arguments (`x` and `y`), that returns `sqrt (x*x+y*y)`.
### Continuations in argument lists
**TODO** : describe how the syntax `{x... (f) y...: z}` is equivalent to
`{x...: f {y...: z}}`, and why it can be useful.
Source Directives
=================
......
......@@ -15,7 +15,7 @@ If you're feeling lucky, you can try to run the following command in a
terminal, which will perform all the necessary steps for you :
~~~~{.terminal}
curl -s https://www.curly-lang.org/install-curly.sh | sh -s - --import-standard-keys
curl -s https://www.curly-lang.org/install-curly.sh | sh -s - --standard-keys
~~~~~
Compiling from source, for the curious
......
#!/bin/sh
curly_version="0.59.4.3"
curly_version="0.59.4.4"
curly_url="https://www.curly-lang.org/pkg/curly-$curly_version.tar.xz"
import_stdkeys=
install_completions=
prefix_dir="$HOME/.local"
lib_dir=
bin_dir=
......@@ -17,7 +18,8 @@ while [ "$#" -gt 0 ]; do
*) optname="$o";;
esac
case "$optname" in
--import-standard-keys) import_stdkeys=true;;
--standard-keys) import_stdkeys=true;;
--completions) install_completions=true;;
-p|--prefix) prefix_dir="$optval";;
-L|--lib-dir) lib_dir="$optval";;
-B|--bin-dir) bin_dir="$optval";;
......@@ -51,3 +53,6 @@ trace ln -fs "$lib_dir/curly-$curly_version/curly" "$bin_dir/curly"
if [ -n "$import_stdkeys" ]; then
"$bin_dir/curly" %'key import curly-std standard.curly-lang.org'
fi
if [ -n "$install_completions" ]; then
"$bin_dir/curly" --goody install.sh | sh -s bash-completions
fi
......@@ -10,39 +10,44 @@ import System.Environment (lookupEnv)
csi = "\x1b["
data ANSITerm = ANSITerm Bool
tc2c :: Bool -> TermColor -> String
tc2c True Black = "30m"
tc2c False Black = "40m"
tc2c True Red = "31m"
tc2c False Red = "41m"
tc2c True Green = "32m"
tc2c False Green = "42m"
tc2c True Yellow = "33m"
tc2c False Yellow = "43m"
tc2c True Blue = "34m"
tc2c False Blue = "44m"
tc2c True Magenta = "35m"
tc2c False Magenta = "45m"
tc2c True Cyan = "36m"
tc2c False Cyan = "46m"
tc2c True White = "37m"
tc2c False White = "47m"
tc2c True (ColorNumber n) = "38;5;"+show n+"m"
data ANSITerm = ColorTerm | BWTerm | DummyTerm
data Layer = Fg | Bg
tc2c :: Layer -> TermColor -> String
tc2c Fg Black = "30m"
tc2c Bg Black = "40m"
tc2c Fg Red = "31m"
tc2c Bg Red = "41m"
tc2c Fg Green = "32m"
tc2c Bg Green = "42m"
tc2c Fg Yellow = "33m"
tc2c Bg Yellow = "43m"
tc2c Fg Blue = "34m"
tc2c Bg Blue = "44m"
tc2c Fg Magenta = "35m"
tc2c Bg Magenta = "45m"
tc2c Fg Cyan = "36m"
tc2c Bg Cyan = "46m"
tc2c Fg White = "37m"
tc2c Bg White = "47m"
tc2c Fg (ColorNumber n) = "38;5;"+show n+"m"
instance Terminal ANSITerm where
setBold _ b = csi + if b then "1m" else "m"
setUnderlined _ b = csi + if b then "4m" else "24m"
setItalic _ b = csi + if b then "3m" else "23m"
setForegroundColor (ANSITerm True) c = csi + tc2c True c
setForegroundColor _ _ = ""
setBackgroundColor (ANSITerm True) c = csi + tc2c False c
setBackgroundColor _ _ = ""
restoreDefaultColors (ANSITerm True) = csi + "39m"
restoreDefaultColors _ = ""
setBold DummyTerm _ = ""
setBold _ b = csi + if b then "1m" else "m"
setUnderlined DummyTerm _ = ""
setUnderlined _ b = csi + if b then "4m" else "24m"
setItalic DummyTerm _ = ""
setItalic _ b = csi + if b then "3m" else "23m"
setForegroundColor ColorTerm c = csi + tc2c Fg c
setForegroundColor _ _ = ""
setBackgroundColor ColorTerm c = csi + tc2c Bg c
setBackgroundColor _ _ = ""
restoreDefaultColors ColorTerm = csi + "39m"
restoreDefaultColors _ = ""
setupTerm :: String -> IO ANSITerm
setupTerm "vt100" = return (ANSITerm False)
setupTerm ('e':'t':'e':'r':'m':'-':_) = return (ANSITerm False)
setupTerm _ = return (ANSITerm True)
setupTerm "vt100" = return BWTerm
setupTerm ('e':'t':'e':'r':'m':'-':_) = return BWTerm
setupTerm "" = return DummyTerm
setupTerm _ = return ColorTerm
setupTermFromEnv :: IO ANSITerm
setupTermFromEnv = setupTerm . fromMaybe "vt100" =<< lookupEnv "TERM"
setupTermFromEnv = setupTerm . fromMaybe "" =<< lookupEnv "TERM"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment