added two string functions, doc updates

This commit is contained in:
VaclavT 2021-05-10 19:07:50 +02:00
parent 783aa6976b
commit a752ebdf12
7 changed files with 98 additions and 42 deletions

View File

@ -1,15 +1,12 @@
### BUGS ### BUGS
- (read-file "nonexisting/file.csv") shows only "could not open file" - should print filename - (read-file "nonexisting/file.csv") shows only "could not open file" - should print filename
- error: the expression `("/usr/local/var/mlisp")` with message "cannot cast" - should add to what is casting
- better error reporting..for example ls_dir on non existing dir should prind `pwd` dir - better error reporting..for example ls_dir on non existing dir should prind `pwd` dir
### TODO ### TODO
- add debug support, at least call stack - add debug support, at least call stack
- add stdtest - to test every functionality
- add parenthesis coloring into linenoise
- multiline editing (kilo editor) - multiline editing (kilo editor)
- execute system command should capture stderr - execute system command should capture stderr
- add some mem stats to benchmark - add some mem stats to benchmark

View File

@ -57,7 +57,7 @@
|`(include file)`|Read a file and execute its code|| |`(include file)`|Read a file and execute its code||
|`(read-file filename)`|Get the contents of a file|| |`(read-file filename)`|Get the contents of a file||
|`(write-file filename)`|Write a string to a file|| |`(write-file filename)`|Write a string to a file||
|`(read-url url [headers])`|Reads URL|Returnd list (status_code content)| |`(read-url url [headers])`|Reads URL|Returns list (status_code content)|
|`(system-cmd command_str)`|Execute system command|| |`(system-cmd command_str)`|Execute system command||
|`(ls-dir ..)`|List a dir|List of directory entries| |`(ls-dir ..)`|List a dir|List of directory entries|
|`(is-file? ..)`|Returns true if passed filename is a file|| |`(is-file? ..)`|Returns true if passed filename is a file||
@ -77,7 +77,7 @@
|`(string-pad str len char rpad_lpad)`||| |`(string-pad str len char rpad_lpad)`|||
|`(string-lpad str len char)`|Pad string from start with char to length len|`>>> (string-lpad "0" 10 "x") => "xxxxxxxxx0"`| |`(string-lpad str len char)`|Pad string from start with char to length len|`>>> (string-lpad "0" 10 "x") => "xxxxxxxxx0"`|
|`(string-rpad str len char)`|Pad string from righ with char to length len|`>>> (string-rpad "0" 10 "x") => "0xxxxxxxxx"`| |`(string-rpad str len char)`|Pad string from righ with char to length len|`>>> (string-rpad "0" 10 "x") => "0xxxxxxxxx"`|
|`(string-split str separator)`||| |`(string-split str separator)`|Splits string into list by regexp|`>>> (string-split "split me by space" "\s+") => ("split" "me" "by" "space")`|
`(string-rltrim str len RKRRKR)`| || `(string-rltrim str len RKRRKR)`| ||
`(string-ltrim str)`|Removes " \n\r\t" from the begininfg of str|| `(string-ltrim str)`|Removes " \n\r\t" from the begininfg of str||
`(string-rtrim str)`|Removes " \n\r\t" from the end of str|| `(string-rtrim str)`|Removes " \n\r\t" from the end of str||
@ -85,24 +85,26 @@
`(string-case str RKRRKR)`||| `(string-case str RKRRKR)`|||
`(string-upcase str)`|Returns up cased string|`>>> (string-upcase "abcdefghchijklmn") => "ABCDEFGHCHIJKLMN"`| `(string-upcase str)`|Returns up cased string|`>>> (string-upcase "abcdefghchijklmn") => "ABCDEFGHCHIJKLMN"`|
`(string-downcase str)`|Returns down cased string |`>>> (string-downcase "ABCDefghchijklmn") => "abcdefghchijklmn"`| `(string-downcase str)`|Returns down cased string |`>>> (string-downcase "ABCDefghchijklmn") => "abcdefghchijklmn"`|
`(string-join lst sep)`|Returns string created as elements of concatenation of lst elements separated by sep|| `(string-join lst sep)`|Returns string created as elements of concatenation of lst elements separated by sep|`>>> (string-join ("A" "B" "C" "D") ",") => "A,B,C,D"`|
`(endl)`||| `(string-len str)`|Returns string length|`>>> (string-len "abcdef") => 6`|
`(string-substr str pos len`|Returns substring from str starting at pos with len. If pos is negative returns substring from the end of string|`>>> (string-substr "ABCD" -2 2) => "CD"`|
`(string-find str lookup pos`|Returns position of lookup in str starting on position. First char index is 0. If not found returns nil|`>>> (string-find " long long int;" "long" 2) => 6`|
|`(int value)`|Cast an item to an int|`>>> (int 3.41) => 3`| |`(int value)`|Cast an item to an int|`>>> (int 3.41) => 3`|
|`(float value)`|Cast item to a float|`>>> (int 3.41) => 3.14`| |`(float value)`|Cast item to a float|`>>> (int 3.41) => 3.14`|
|`(string value)`|Cast int or float item to a string|`>>> (string 3.14) => "3.14"`| |`(string value)`|Cast int or float item to a string|`>>> (string 3.14) => "3.14"`|
|`(eval <exp>)`|Eval returns the value of the second evaluation|`>>> (eval '(+ 1 2)) => 3`| |`(eval <exp>)`|Eval returns the value of the second evaluation|`>>> (eval '(+ 1 2)) => 3`|
|`(type e)`|Returns data type of e|`>>> (type (+ 1 2)) => "int"`| |`(type e)`|Returns data type of e|`>>> (type (+ 1 2)) => "int"`|
|`(parse ..)`||| |`(parse ..)`||`>>> (eval (first (parse "(+ 1 2)"))) => 3`|
|`(make-list-of size value)`|Makes list with size elements of values|| |`(make-list-of size value)`|Makes list with size elements of values|`>>> (make-list-of 5 0) => (0 0 0 0 0)`|
|`(make-list size)`|Makes list of nil values with size length|| |`(make-list size)`|Makes list of nil values with size length|`>>> (make-list 5) => (nil nil nil nil nil)`|
|`(empty-list? lst)`|Return true is lst is list with zero elements|| |`(empty-list? lst)`|Return true is lst is list with zero elements|`>>> (empty-list? '()) => 1`|
|`(memeber lst item)`|Returns 1 when item is inluded in lst otherwise 0|| |`(memeber lst item)`|Returns 1 when item is inluded in lst otherwise 0||
|`(uniq list)`|Filter out any duplicates from list|`>>> (uniq '(1 2 2 3 4 5 2 2)) => (1 2 3 4 5)`| |`(uniq list)`|Filter out any duplicates from list|`>>> (uniq '(1 2 2 3 4 5 2 2)) => (1 2 3 4 5)`|
|`(flatten list)`||`>>> (flatten '(1 (2 2) 3)) => (1 2 2 3)`| |`(flatten list)`||`>>> (flatten '(1 (2 2) 3)) => (1 2 2 3)`|
|`(quick-sort-by list cmp)`||| |`(quick-sort-by list cmp)`|||
|`(quick-sort list)`|return sorted list|`>>> (quick-sort '(2 4 6 1 7 3 3 9 5)) => (1 2 3 3 4 5 6 7 9)`| |`(quick-sort list)`|return sorted list|`>>> (quick-sort '(2 4 6 1 7 3 3 9 5)) => (1 2 3 3 4 5 6 7 9)`|
|`(not c)`|Logical NOT of c|| |`(not c)`|Logical NOT of c|`>>> (not 1) => nil`|
|`(new n)`|Negates number|| |`(neg n)`|Negates number|`>>> (neg -5) => 5`|
|`(is-pos? n)`|Returns true if n is positive number|| |`(is-pos? n)`|Returns true if n is positive number||
|`(is-neg? n)`|Returns true if n is negative number|| |`(is-neg? n)`|Returns true if n is negative number||
|`(dec n)`|Return n decremented by 1|`>>> (dec 5) => 4`| |`(dec n)`|Return n decremented by 1|`>>> (dec 5) => 4`|
@ -115,29 +117,6 @@
|`(fifth l)`|Returns fifth element of list|| |`(fifth l)`|Returns fifth element of list||
|`(nth i l)`|Return i-th elemenet of list. First element has index 1|| |`(nth i l)`|Return i-th elemenet of list. First element has index 1||
|`( 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 ..)`||`>>> (sprintf "%s, %d, %.2f" (list "string" 1000 3.14)) => "string, 1000, 3.14"`|
|`(xx ..)`||| |`(xx ..)`|||
```
(define term-rst-esc "\x1B[0m")
(define term-red-esc '"\x1B[31m")
(define term-green-esc "\x1B[32m")
(define term-yellow-esc "\x1B[33m")
(define term-blue-esc "\x1B[34m")
(define term-magenta-esc "\x1B[35m")
(define term-cyan-esc "\x1B[36m")
(define term-white-esc "\x1B[37m")
(define term-bold-esc "\x1B[1m")
(define term-underline-esc "\x1B[4m")
(defun term-red (str) (sprintf (+ term-red-esc str term-rst-esc)))
(defun term-green (str) (sprintf (+ term-green-esc str term-rst-esc)))
(defun term-yellow (str) (sprintf (+ term-yellow-esc str term-rst-esc)))
(defun term-blue (str) (sprintf (+ term-blue-esc str term-rst-esc)))
(defun term-magenta (str) (sprintf (+ term-magenta-esc str term-rst-esc)))
(defun term-cyan (str) (sprintf (+ term-cyan-esc str term-rst-esc)))
(defun term-white (str) (sprintf (+ term-white-esc str term-rst-esc)))
(defun term-bold (str) (sprintf (+ term-bold-esc str term-rst-esc)))
(defun term-underline (str) (sprintf (+ term-underline-esc str term-rst-esc)))
```

44
ml.cpp
View File

@ -40,6 +40,7 @@
#define INTERNAL_ERROR "internal virtual machine error" #define INTERNAL_ERROR "internal virtual machine error"
#define INDEX_OUT_OF_RANGE "index out of range" #define INDEX_OUT_OF_RANGE "index out of range"
#define MALFORMED_PROGRAM "malformed program" #define MALFORMED_PROGRAM "malformed program"
#define NOT_IMPLEMENTED_YET_ERROR "not implemented yet"
#define STRING_TYPE "string" #define STRING_TYPE "string"
@ -1552,9 +1553,6 @@ namespace builtin {
throw MlError(args[0], env, INVALID_ARGUMENT); throw MlError(args[0], env, INVALID_ARGUMENT);
std::vector<MlValue> parsed = ::parse(args[0].as_string()); std::vector<MlValue> parsed = ::parse(args[0].as_string());
// if (parsed.size() == 1)
// return parsed[0];
// else return MlValue(parsed);
return MlValue(parsed); return MlValue(parsed);
} }
@ -1602,11 +1600,46 @@ namespace builtin {
eval_args(args, env); eval_args(args, env);
if (args.size() != 2) if (args.size() != 2)
throw MlError(MlValue("string_case", string_case), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); throw MlError(MlValue("string-case", string_case), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
return MlValue::string(string_lucase(args[0].as_string(), args[1].as_string())); return MlValue::string(string_lucase(args[0].as_string(), args[1].as_string()));
} }
MlValue string_len(std::vector<MlValue> args, MlEnvironment &env) {
eval_args(args, env);
if (args.size() != 1)
throw MlError(MlValue("string-len", string_len), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
return MlValue{(long)args[0].as_string().size()};
}
MlValue string_substr(std::vector<MlValue> args, MlEnvironment &env) {
eval_args(args, env);
if (args.size() < 1 || args.size() > 3)
throw MlError(MlValue("string-substr", string_substr), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
const std::string& str = args[0].as_string();
long pos = args.size() > 1 ? args[1].as_int() : 0;
long count = args.size() > 2 ? args[2].as_int() : str.size();
return MlValue::string(string_substring(str, pos, count));
}
MlValue string_find(std::vector<MlValue> args, MlEnvironment &env) {
eval_args(args, env);
if (args.size() < 2 || args.size() > 3)
throw MlError(MlValue("string-find", string_find), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS);
size_t start_pos = args.size() > 2 ? args[2].as_int() : 0;
size_t pos = string_find_substr(args[0].as_string(), args[1].as_string(), start_pos);
return pos == -1 ? MlValue::nil() : MlValue((long)pos);
}
// trims characters " \n\r\t" from left or right or both ends of a string // trims characters " \n\r\t" from left or right or both ends of a string
MlValue string_rltrim(std::vector<MlValue> args, MlEnvironment &env) { MlValue string_rltrim(std::vector<MlValue> args, MlEnvironment &env) {
eval_args(args, env); eval_args(args, env);
@ -1966,6 +1999,9 @@ MlValue MlEnvironment::get(const std::string &name) const {
if (name == "string-pad") return MlValue("string-pad", builtin::string_pad); if (name == "string-pad") return MlValue("string-pad", builtin::string_pad);
if (name == "string-rltrim") return MlValue("string-rltrim", builtin::string_rltrim); if (name == "string-rltrim") return MlValue("string-rltrim", builtin::string_rltrim);
if (name == "string-case") return MlValue("string-case", builtin::string_case); if (name == "string-case") return MlValue("string-case", builtin::string_case);
if (name == "string-len") return MlValue("string-len", builtin::string_len);
if (name == "string-substr") return MlValue("string-substr", builtin::string_substr);
if (name == "string-find") return MlValue("string-find", builtin::string_find);
// Casting operations // Casting operations
if (name == "int") return MlValue("int", builtin::cast_to_int); if (name == "int") return MlValue("int", builtin::cast_to_int);

View File

@ -77,3 +77,28 @@ std::string string_padd(const std::string &str, int pad_len, char fill_char, boo
else else
return std::string(pad_len - str.size(), fill_char) + str; return std::string(pad_len - str.size(), fill_char) + str;
} }
std::string string_substring(const std::string & str, long pos, long count) {
size_t start_pos = pos;
if (pos < 0) {
start_pos = str.size() - abs(pos);
}
if ( (start_pos >= str.size()) || (count < 1) || (start_pos < 0) ) {
throw std::invalid_argument("Invalid parameter(s) for string-substr.");
}
return str.substr(start_pos, count);
}
size_t string_find_substr(const std::string & str, const std::string & pattern, long pos) {
if (pos >= str.size()) {
throw std::invalid_argument("Invalid parameter(s) for string-find.");
}
size_t p = str.find(pattern, pos);
return p != str.npos ? p : -1;
}

View File

@ -19,3 +19,7 @@ std::string string_lucase(std::string s, const std::string &strcase);
std::string string_trim(std::string s, const std::string &chars_to_trim, const std::string &rltrim); std::string string_trim(std::string s, const std::string &chars_to_trim, const std::string &rltrim);
std::string string_padd(const std::string & str, int pad_len, char fill_char, bool from_right); std::string string_padd(const std::string & str, int pad_len, char fill_char, bool from_right);
std::string string_substring(const std::string & str, long pos, long count);
size_t string_find_substr(const std::string & str, const std::string & pattern, long pos);

View File

@ -11,6 +11,8 @@
(defun ut::assert-false (test) (ut::assert-equal nil test)) (defun ut::assert-false (test) (ut::assert-equal nil test))
(defun ut::assert-nil (test) (ut::assert-equal nil test))
(defun ut::define-test (name exp_list) (defun ut::define-test (name exp_list)
(set! ut::tests_list (push ut::tests_list (list name exp_list)))) (set! ut::tests_list (push ut::tests_list (list name exp_list))))

View File

@ -54,6 +54,19 @@
(ut::define-test "result of (string-split \"split me by space\" \"\\s+\")" '(ut::assert-equal '("split" "me" "by" "space") (string-split "split me by space" "\\s+"))) (ut::define-test "result of (string-split \"split me by space\" \"\\s+\")" '(ut::assert-equal '("split" "me" "by" "space") (string-split "split me by space" "\\s+")))
(ut::define-test "result of (string-upcase \"abcABCD\")" '(ut::assert-equal "ABCABCD" (string-upcase "abcABCD"))) (ut::define-test "result of (string-upcase \"abcABCD\")" '(ut::assert-equal "ABCABCD" (string-upcase "abcABCD")))
(ut::define-test "result of (string-downcase \"abcABCD\")" '(ut::assert-equal "abcabcd" (string-downcase "abcABCD"))) (ut::define-test "result of (string-downcase \"abcABCD\")" '(ut::assert-equal "abcabcd" (string-downcase "abcABCD")))
(ut::define-test "result of (string-len \"abcdef\")" '(ut::assert-equal 6 (string-len "abcdef")))
(ut::define-test "result of (string-substr \"ABCDEF\")" '(ut::assert-equal "ABCDEF" (string-substr "ABCDEF")))
(ut::define-test "result of (string-substr \"ABCDEF\" 1)" '(ut::assert-equal "BCDEF" (string-substr "ABCDEF" 1)))
(ut::define-test "result of (string-substr \"ABCDEF\" 2 3)" '(ut::assert-equal "CDE" (string-substr "ABCDEF" 2 3)))
(ut::define-test "result of (string-substr \"ABCDEF\"4 42)" '(ut::assert-equal "EF" (string-substr "ABCDEF" 4 42)))
(ut::define-test "result of (string-substr \"ABCDEF\" -2 2)" '(ut::assert-equal "EF" (string-substr "ABCDEF" -2 2)))
(ut::define-test "result of (string-find \" long long int;\" \"long\")" '(ut::assert-equal 1 (string-find " long long int;" "long")))
(ut::define-test "result of (string-find \" long long int;\" \"long\" 2)" '(ut::assert-equal 6 (string-find " long long int;" "long" 2)))
(ut::define-test "result of (string-find \" long long int;\" \" \")" '(ut::assert-equal 0 (string-find " long long int;" " ")))
(ut::define-test "result of (string-find \" long long int;\" \"o\")" '(ut::assert-equal 2 (string-find " long long int;" "o")))
(ut::define-test "result of (string-find \" long long int;\" \"float\")" '(ut::assert-nil (string-find " long long int;" "float")))
(ut::define-test "result of (write-file \"/tmp/file\" \"write-file test\")" '(ut::assert-equal 1 (write-file "/tmp/file" "write-file test\n"))) (ut::define-test "result of (write-file \"/tmp/file\" \"write-file test\")" '(ut::assert-equal 1 (write-file "/tmp/file" "write-file test\n")))
(ut::define-test "result of (is-file? \"/tmp/file\")" '(ut::assert-true (is-file? "/tmp/file"))) (ut::define-test "result of (is-file? \"/tmp/file\")" '(ut::assert-true (is-file? "/tmp/file")))