version 0.2
try, throw implemented help screen updates some code reorganisation readme and doc updates
This commit is contained in:
105
Readme.md
105
Readme.md
@@ -1,41 +1,35 @@
|
|||||||
|
|
||||||
### BUGS
|
## ml
|
||||||
- (read-file "nonexisting/file.csv") shows only "could not open file" - should print filename
|
is a small lisp interpreter based on Adam McDaniel's wisp interpreter (https://github.com/adam-mcdaniel/wisp). It's main purpose is to learn something about more recent c++
|
||||||
- better error reporting..for example ls_dir on non existing dir should prind `pwd` dir
|
|
||||||
|
|
||||||
|
|
||||||
### TODO
|
### Example of use
|
||||||
- add debug support, at least call stack
|
```
|
||||||
- multiline editing (kilo editor)
|
cat <<EOT > /tmp/qs.lsp
|
||||||
- execute system command should capture stderr
|
(defun qs (l)
|
||||||
- add some mem stats to benchmark
|
(if (<= (len l) 1)
|
||||||
- create pastebin like web using ml
|
l
|
||||||
#### Code
|
(do
|
||||||
- documentation
|
(define pivot (first l))
|
||||||
- rename constants in ml_profiler.h
|
(+
|
||||||
- replace to_string macro in ml.cpp
|
(qs (filter (lambda (n) (> pivot n)) l))
|
||||||
- add url of source/inspiration to clib/*.cpp
|
(list pivot)
|
||||||
- add instrumentation (time, nr of evals, num of atoms, debug info, debug environment etc)
|
(qs (tail (filter (lambda (n) (<= pivot n)) l)))
|
||||||
#### Language
|
))
|
||||||
- support for "t" symbol as a true
|
))
|
||||||
- support for exceptions
|
|
||||||
- string functions
|
|
||||||
- compare - needed for sorting, cmp ignore case
|
|
||||||
- regexp functions
|
|
||||||
- date support
|
|
||||||
- decode-universal-time
|
|
||||||
- env functions
|
|
||||||
- get-env, set-env; set-env cannot be implemented in stdlib.lsp, because popen is in fact subshell
|
|
||||||
- add include-stdlib function for other libs in stdlib dir (during startup stdlib.lsp is loaded only)
|
|
||||||
- syntax highlighting do VS Code
|
|
||||||
|
|
||||||
#### Performance
|
(print (qs '(10 9 8 7 6 5 4 3 2 10)))
|
||||||
- define is one of most frequent callee, when in scope with very few vars, lookup sequentially
|
EOT
|
||||||
- first, second are often called -> implement in c++
|
|
||||||
- push_back - repeatedly without reserving size
|
ml -f /tmp/qs.lsp
|
||||||
- range - with for(int i...) and reserving result size can be 3times faster on (range 1 10000)
|
```
|
||||||
- mini_sprintf - unnecesary copying between vector and list
|
#### Interactive mode
|
||||||
- (do, scope ..) repeatedly assign to acc
|
```
|
||||||
|
ml
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
see /doc/Doc.md
|
||||||
|
|
||||||
#### Compile
|
#### Compile
|
||||||
```
|
```
|
||||||
@@ -58,10 +52,42 @@ utils/local_install.sh
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Example of use
|
### KNOWNN BUGS
|
||||||
```
|
|
||||||
ml -f tests/test.lsp
|
|
||||||
```
|
### TODO
|
||||||
|
- add debug support, at least call stack
|
||||||
|
- multiline editing (see kilocpp editor)
|
||||||
|
- execute system command should capture stderr
|
||||||
|
- add some mem stats to benchmark
|
||||||
|
|
||||||
|
#### Code
|
||||||
|
- add documentation
|
||||||
|
- rename constants in ml_profiler.h
|
||||||
|
- replace to_string macro in ml.cpp
|
||||||
|
- add instrumentation (time, nr of evals, num of atoms, debug info, debug environment etc)
|
||||||
|
|
||||||
|
#### Language
|
||||||
|
- support for "t" symbol as a true
|
||||||
|
- support for exceptions
|
||||||
|
- string functions
|
||||||
|
- compare - needed for sorting, cmp ignore case
|
||||||
|
- regexp match, regexp tokens
|
||||||
|
- date support
|
||||||
|
- decode-universal-time (http://www.lispworks.com/documentation/HyperSpec/Body/f_dec_un.htm)
|
||||||
|
- env functions
|
||||||
|
- get-env, set-env; set-env cannot be implemented in stdlib.lsp, because popen is in fact subshell
|
||||||
|
- add include-stdlib function for other libs in stdlib dir (during startup stdlib.lsp is loaded only)
|
||||||
|
- syntax highlighting do VS Code
|
||||||
|
|
||||||
|
#### Performance
|
||||||
|
- define is one of most frequent callee, when in scope with very few vars, lookup sequentially
|
||||||
|
- first, second are often called -> implement in c++
|
||||||
|
- push_back - repeatedly without reserving size
|
||||||
|
- range - with for(int i...) and reserving result size can be 3times faster on (range 1 10000)
|
||||||
|
- mini_sprintf - unnecesary copying between vector and list
|
||||||
|
- (do, scope ..) repeatedly assign to acc
|
||||||
|
|
||||||
|
|
||||||
### Links
|
### Links
|
||||||
https://github.com/adam-mcdaniel/wisp
|
https://github.com/adam-mcdaniel/wisp
|
||||||
@@ -69,6 +95,5 @@ https://github.com/dropbox/json11
|
|||||||
https://github.com/antirez/linenoise
|
https://github.com/antirez/linenoise
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### read stdout and stderr from popen
|
#### read stdout and stderr from popen
|
||||||
https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
|
https://stackoverflow.com/questions/478898how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
|
||||||
|
|||||||
24
debug.lsp
24
debug.lsp
@@ -1,6 +1,20 @@
|
|||||||
(write-file "/tmp/f.txt" "line 1\nline 2\nline3")
|
(define fc 5)
|
||||||
(read-file-lines "/tmp/f.txt" (lambda (ln) (print ln)))
|
(define sc 5)
|
||||||
|
|
||||||
(define counter (lambda (ln) (do (set! ii (+ ii 1)) t ii))))
|
(define first_tid (thread-create (while (> fc 0) (do (thread-under-lock "ilock" (do (set! fc (dec fc))(print 1))) (thread-sleep 500)))))
|
||||||
(define ii 0)
|
(define second_tid (thread-create (while (> sc 0) (do (thread-under-lock "ilock" (do (set! sc (dec sc))(print 2))) (thread-sleep 750)))))
|
||||||
(print (read-file-lines "/tmp/f.txt" counter))
|
|
||||||
|
(print "first thread id:" first_tid)
|
||||||
|
(print "second thread id:" second_tid)
|
||||||
|
(threads-join)
|
||||||
|
(print "ok")
|
||||||
|
|
||||||
|
(try
|
||||||
|
(ls-dir "nonexisting/dir")
|
||||||
|
(print "dir not exists"))
|
||||||
|
|
||||||
|
(try
|
||||||
|
(throw "my-exception")
|
||||||
|
(print "exception caught:" ml-exception))
|
||||||
|
|
||||||
|
(/ 1 0)
|
||||||
37
doc/Doc.md
37
doc/Doc.md
@@ -1,6 +1,24 @@
|
|||||||
|
## Command line options
|
||||||
|
|option|Description|
|
||||||
|
|:-|-|
|
||||||
|
|-h|Prints help|
|
||||||
|
|-b|Skips loadin of std lib|
|
||||||
|
|-c code|Runs given code|
|
||||||
|
|-f source_file ..|Executes code in files|
|
||||||
|
|-i|Runs REPL|
|
||||||
|
|-p|Prints profile info at the end|
|
||||||
|
|-v|Prints version string|
|
||||||
|
|
||||||
|
|
||||||
|
## REPL commands
|
||||||
|
|Command|Description|
|
||||||
|
|:-|-|
|
||||||
|
|!q, !quit|Quit REPL|
|
||||||
|
|!e, !env|Display environment|
|
||||||
|
|!x, !export|Exports command from this session. Asks for filename to save|
|
||||||
|
|
||||||
|
|
||||||
## Syntax and Special Forms
|
## Syntax and Special Forms
|
||||||
|
|
||||||
|Special Form|Argument Evaluations|Purpose|
|
|Special Form|Argument Evaluations|Purpose|
|
||||||
|:-|-|-|
|
|:-|-|-|
|
||||||
|`(if cond a b)`|`if` only evaluates its `cond` argument. If `cond` is truthy (non-zero), then `a` is evaluated. Otherwise, `b` is evaluated.|This special form is the main method of control flow.|
|
|`(if cond a b)`|`if` only evaluates its `cond` argument. If `cond` is truthy (non-zero), then `a` is evaluated. Otherwise, `b` is evaluated.|This special form is the main method of control flow.|
|
||||||
@@ -123,12 +141,17 @@
|
|||||||
|`(inc n)`|Return n incremented by 1|`>>> (inc 5) => 6`|
|
|`(inc n)`|Return n incremented by 1|`>>> (inc 5) => 6`|
|
||||||
|`(sleep time)`|Pauses execution for time interval of seconds||
|
|`(sleep time)`|Pauses execution for time interval of seconds||
|
||||||
|`(get-env var)`|Return environment variable var|`>>> (get-env "HOME") => "/Users/vaclavt"`|
|
|`(get-env var)`|Return environment variable var|`>>> (get-env "HOME") => "/Users/vaclavt"`|
|
||||||
|`(second l)`|Returns second element of list||
|
|`(second list)`|Returns second element of list|`>>> (second '(1 2 3 4 5 6 7)) => 2`|
|
||||||
|`(third l)`|Returns third element of list||
|
|`(third list)`|Returns third element of list|`>>> (third '(1 2 3 4 5 6 7)) => 3`|
|
||||||
|`(fourth l)`|Returns fourth element of list||
|
|`(fourth list)`|Returns fourth element of list|`>>> (fourth '(1 2 3 4 5 6 7)) => 4`|
|
||||||
|`(fifth l)`|Returns fifth element of list||
|
|`(fifth list)`|Returns fifth element of list|`>>> (fifth '(1 2 3 4 5 6 7)) => 5`|
|
||||||
|`(nth i l)`|Return i-th elemenet of list. First element has index 1||
|
|`(nth i list)`|Return i-th elemenet of list. First element has index 1|`>>> (nth 7 '(1 2 3 4 5 6 7)) => 7`|
|
||||||
|`( make-csv list)`|creates csv string from list of lists|(print (make-csv '(("r1c1" "r1c2") ("r2c1" "r2c2"))))
|
|`( make-csv list)`|creates csv string from list of lists|(print (make-csv '(("r1c1" "r1c2") ("r2c1" "r2c2"))))
|
||||||
|`(sprintf ..)`||`>>> (sprintf "%s, %d, %.2f" (list "string" 1000 3.14)) => "string, 1000, 3.14"`|
|
|`(sprintf ..)`||`>>> (sprintf "%s, %d, %.2f" (list "string" 1000 3.14)) => "string, 1000, 3.14"`|
|
||||||
|
|`(thread-create code..)`|Creates new thread, starts evalueating code and returns immediatelly thread id||
|
||||||
|
|`(thread-under-lock lockname code)`|Acquire lock with lockname and eval code. ilock currently is only allowd lockname||
|
||||||
|
|`(thread-sleep milisecons)`|Sleeps thread for given amount of miliseconds||
|
||||||
|
|`(threads-join)`|Wait for all running threads to finish||
|
||||||
|
|`(try block catch_block [finally_block])`|Evaluates block and if an exception is thrown, evaluates catch_block.Eventually in both cases evals finally_block. Return evaluated last expression from block if no exception or last expression from catch_block, if exception is thrown from block. Variable ml-exception in catch block is available with astring describing exception||
|
||||||
|
|`(throw-exception exp_desc)`|Throws an exception with exp_desc describing what happened ||
|
||||||
|`(xx ..)`|||
|
|`(xx ..)`|||
|
||||||
|
|||||||
129
ml.cpp
129
ml.cpp
@@ -1351,6 +1351,11 @@ namespace builtin {
|
|||||||
|
|
||||||
if (args.size() != 2)
|
if (args.size() != 2)
|
||||||
throw MlError(MlValue("/", divide), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
throw MlError(MlValue("/", divide), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||||
|
|
||||||
|
if ((args[1].get_type_name() == "int" && args[1] == 0l) ||
|
||||||
|
(args[1].get_type_name() == "float" && args[1] == 0.0))
|
||||||
|
throw std::invalid_argument("divide by zero.");
|
||||||
|
|
||||||
return args[0] / args[1];
|
return args[0] / args[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1846,17 +1851,6 @@ namespace builtin {
|
|||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
MlValue threads_join(std::vector<MlValue> args, MlEnvironment &env) {
|
|
||||||
if (args.size() != 0)
|
|
||||||
throw MlError(MlValue("threads_join", threads_join), env, TOO_MANY_ARGS);
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lockGuard(register_mutex);
|
|
||||||
for (auto &th : threads_register)
|
|
||||||
if (th.joinable()) th.join();
|
|
||||||
|
|
||||||
return MlValue::nil();
|
|
||||||
}
|
|
||||||
|
|
||||||
MlValue thread_sleep(std::vector<MlValue> args, MlEnvironment &env) {
|
MlValue thread_sleep(std::vector<MlValue> args, MlEnvironment &env) {
|
||||||
eval_args(args, env);
|
eval_args(args, env);
|
||||||
|
|
||||||
@@ -1866,48 +1860,51 @@ namespace builtin {
|
|||||||
std::this_thread::sleep_for(std::chrono::milliseconds(args[0].as_int()));
|
std::this_thread::sleep_for(std::chrono::milliseconds(args[0].as_int()));
|
||||||
return args[0];
|
return args[0];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void repl(MlEnvironment &env) {
|
MlValue threads_join(std::vector<MlValue> args, MlEnvironment &env) {
|
||||||
std::string code;
|
if (args.size() != 0)
|
||||||
std::string input;
|
throw MlError(MlValue("threads-join", threads_join), env, TOO_MANY_ARGS);
|
||||||
MlValue tmp;
|
|
||||||
std::vector<MlValue> parsed;
|
|
||||||
|
|
||||||
setup_linenoise(env);
|
std::lock_guard<std::mutex> lockGuard(register_mutex);
|
||||||
|
for (auto &th : threads_register)
|
||||||
|
if (th.joinable()) th.join();
|
||||||
|
|
||||||
while (true) {
|
return MlValue::nil();
|
||||||
char *line = linenoise(">>> ");
|
}
|
||||||
if (line == nullptr) break;
|
|
||||||
|
|
||||||
linenoise_line_read(line);
|
|
||||||
|
|
||||||
input = std::string(line);
|
MlValue try_block(std::vector<MlValue> args, MlEnvironment &env) {
|
||||||
|
if (args.size() < 2 || args.size() > 3)
|
||||||
|
throw MlError(MlValue("try", try_block), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||||
|
|
||||||
if (input == "!quit" || input == "!q")
|
MlValue value;
|
||||||
break;
|
size_t cs_posisition; // unroll stack position to state when try begun to prevent "forgotten" entries in case of exception
|
||||||
else if (input == "!env" || input == "!e")
|
|
||||||
std::cout << env << std::endl;
|
|
||||||
else if (input == "!export" || input == "!x") {
|
|
||||||
std::cout << "File to export to: ";
|
|
||||||
std::getline(std::cin, input);
|
|
||||||
|
|
||||||
write_file_contents(input, code);
|
// try block
|
||||||
} else if (input != "") {
|
|
||||||
try {
|
try {
|
||||||
tmp = run(input, env);
|
cs_posisition = MlPerfMon::instance().get_callstack_position();
|
||||||
std::cout << " => " << tmp.debug() << std::endl;
|
value = args[0].eval(env);
|
||||||
code += input + "\n";
|
// catch block
|
||||||
} catch (MlError &e) {
|
|
||||||
std::cerr << e.description() << std::endl;
|
|
||||||
MlPerfMon::instance().clear_callstack();
|
|
||||||
} catch (std::exception &e) {
|
} catch (std::exception &e) {
|
||||||
std::cerr << e.what() << std::endl;
|
MlPerfMon::instance().restore_callstack_position(cs_posisition);
|
||||||
}
|
env.set("ml-exception", MlValue::string(e.what()));
|
||||||
|
value = args[1].eval(env);
|
||||||
}
|
}
|
||||||
|
// finally block
|
||||||
|
if (args.size() == 3) {
|
||||||
|
args[2].eval(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
MlValue throw_exception(std::vector<MlValue> args, MlEnvironment &env) {
|
||||||
|
if (args.size() != 1)
|
||||||
|
throw MlError(MlValue("throw", throw_exception), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
|
||||||
|
|
||||||
|
throw std::runtime_error(args[0].as_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
close_linenoise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_std_lib(MlEnvironment &env) {
|
void load_std_lib(MlEnvironment &env) {
|
||||||
@@ -2037,6 +2034,10 @@ MlValue MlEnvironment::get(const std::string &name) const {
|
|||||||
if (name == "thread-sleep") return MlValue("thread-sleep", builtin::thread_sleep);
|
if (name == "thread-sleep") return MlValue("thread-sleep", builtin::thread_sleep);
|
||||||
if (name == "threads-join") return MlValue("threads-join", builtin::threads_join);
|
if (name == "threads-join") return MlValue("threads-join", builtin::threads_join);
|
||||||
|
|
||||||
|
// Exceptions
|
||||||
|
if (name == "try") return MlValue("try", builtin::try_block);
|
||||||
|
if (name == "throw") return MlValue("throw", builtin::throw_exception);
|
||||||
|
|
||||||
std::map<std::string, MlValue>::const_iterator itr = defs.find(name);
|
std::map<std::string, MlValue>::const_iterator itr = defs.find(name);
|
||||||
if (itr != defs.end()) return itr->second;
|
if (itr != defs.end()) return itr->second;
|
||||||
else if (parent_scope != nullptr) {
|
else if (parent_scope != nullptr) {
|
||||||
@@ -2061,6 +2062,50 @@ std::vector<std::string> MlEnvironment::get_lambdas_list() const {
|
|||||||
return lambdas;
|
return lambdas;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void repl(MlEnvironment &env) {
|
||||||
|
std::string code;
|
||||||
|
std::string input;
|
||||||
|
MlValue tmp;
|
||||||
|
std::vector<MlValue> parsed;
|
||||||
|
|
||||||
|
setup_linenoise(env);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
char *line = linenoise(">>> ");
|
||||||
|
if (line == nullptr) break;
|
||||||
|
|
||||||
|
linenoise_line_read(line);
|
||||||
|
|
||||||
|
input = std::string(line);
|
||||||
|
|
||||||
|
if (input == "!quit" || input == "!q")
|
||||||
|
break;
|
||||||
|
else if (input == "!env" || input == "!e")
|
||||||
|
std::cout << env << std::endl;
|
||||||
|
else if (input == "!export" || input == "!x") {
|
||||||
|
std::cout << "File to export to: ";
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
|
||||||
|
write_file_contents(input, code);
|
||||||
|
} else if (input != "") {
|
||||||
|
try {
|
||||||
|
tmp = run(input, env);
|
||||||
|
std::cout << " => " << tmp.debug() << std::endl;
|
||||||
|
code += input + "\n";
|
||||||
|
} catch (MlError &e) {
|
||||||
|
std::cerr << e.description() << std::endl;
|
||||||
|
MlPerfMon::instance().clear_callstack();
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close_linenoise();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool cmdOptionExists(char **begin, char **end, const std::string &option) { return std::find(begin, end, option) != end; }
|
bool cmdOptionExists(char **begin, char **end, const std::string &option) { return std::find(begin, end, option) != end; }
|
||||||
|
|
||||||
std::vector<std::string> getCmdOption(char *argv[], int argc, const std::string &option) {
|
std::vector<std::string> getCmdOption(char *argv[], int argc, const std::string &option) {
|
||||||
@@ -2092,7 +2137,7 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
// help
|
// help
|
||||||
if (cmdOptionExists(argv, argv + argc, "-h")) {
|
if (cmdOptionExists(argv, argv + argc, "-h")) {
|
||||||
std::cout << "Usage:\n\t-h print this help\n\t-f source_file - executes code in file\n\t-c code - runs passed code\n\t-i runs repl\n\t-b skip stdlib loading\n\t-p prints profile info at the end\n\t-v prints version string\n\n";
|
std::cout << "Usage:\n\t-h print this help\n\t-b skip stdlib loading\n\t-c code - runs code passed on command line\n\t-f source_file - executes code in file\n\t-i runs repl\n\t-p prints profile info at the end\n\t-v prints version string\n\n";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// version
|
// version
|
||||||
|
|||||||
2
ml.h
2
ml.h
@@ -7,7 +7,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
const std::string VERSION = "ml 0.1 (" __DATE__ " " __TIME__ ")";
|
const std::string VERSION = "ml 0.2 (" __DATE__ " " __TIME__ ")";
|
||||||
|
|
||||||
const std::string STDLIB_LOADER =
|
const std::string STDLIB_LOADER =
|
||||||
R"(
|
R"(
|
||||||
|
|||||||
@@ -58,6 +58,16 @@ std::string MlPerfMon::callstack() const {
|
|||||||
return cs.append("\n");
|
return cs.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t MlPerfMon::get_callstack_position() {
|
||||||
|
return call_stack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MlPerfMon::restore_callstack_position(size_t to_position) {
|
||||||
|
if (to_position < call_stack.size()) {
|
||||||
|
call_stack.resize(to_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MlPerfMon::clear_callstack() {
|
void MlPerfMon::clear_callstack() {
|
||||||
call_stack.empty();
|
call_stack.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,13 @@ public:
|
|||||||
|
|
||||||
void add_method_call(const std::string &method);
|
void add_method_call(const std::string &method);
|
||||||
void end_method_call();
|
void end_method_call();
|
||||||
|
|
||||||
std::string callstack() const;
|
std::string callstack() const;
|
||||||
|
|
||||||
|
size_t get_callstack_position();
|
||||||
|
void restore_callstack_position(size_t to_position);
|
||||||
void clear_callstack();
|
void clear_callstack();
|
||||||
|
|
||||||
void print_results();
|
void print_results();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user