diff --git a/baseio/builtins_baseio.go b/baseio/builtins_baseio.go index 4653735d..ff4bbd17 100644 --- a/baseio/builtins_baseio.go +++ b/baseio/builtins_baseio.go @@ -204,6 +204,14 @@ var builtins_baseio = map[string]*env.Builtin{ // Process / shell // ------------------------------------------------------------------------- + // Tests: + // equal { scmd "exit 0" } 0 + // equal { scmd "exit 3" } 3 + // error { scmd 123 } + // Args: + // * cmd: Shell command string to execute + // Returns: + // * integer exit status code "scmd": { Argsn: 1, Doc: "Execute a shell command and return its exit status code.", @@ -229,6 +237,13 @@ var builtins_baseio = map[string]*env.Builtin{ }, }, + // Tests: + // equal { scmd\capture "echo hi" } "hi\n" + // error { scmd\capture 123 } + // Args: + // * cmd: Shell command string to execute and capture output + // Returns: + // * string containing captured stdout (stderr on error) "scmd\\capture": { Argsn: 1, Doc: "Execute a shell command and capture the output, return it as string", @@ -259,6 +274,8 @@ var builtins_baseio = map[string]*env.Builtin{ }, }, + // Example: + // ; exit 0 "exit": { // ** Argsn: 1, Doc: "Exits the process with the given integer status code (or 0 for any non-integer).", @@ -280,6 +297,8 @@ var builtins_baseio = map[string]*env.Builtin{ // Rye-itself - args / history (requires os.Args / process context) // ------------------------------------------------------------------------- + // Example: + // Rye-itself//args? "Rye-itself//args?": { Argsn: 0, Doc: "Returns command line arguments as a block of parsed values. Each argument is converted to appropriate type (integer, float, or string).", @@ -288,6 +307,8 @@ var builtins_baseio = map[string]*env.Builtin{ }, }, + // Example: + // Rye-itself//Args? "Rye-itself//Args?": { Argsn: 0, Doc: "Returns command line arguments as a block of parsed values. Each argument is converted to appropriate type (integer, float, or string).", @@ -296,6 +317,8 @@ var builtins_baseio = map[string]*env.Builtin{ }, }, + // Example: + // Rye-itself//Args\\raw? "Rye-itself//Args\\raw?": { Argsn: 1, Doc: "Returns raw command line arguments joined as a single string.", @@ -308,6 +331,8 @@ var builtins_baseio = map[string]*env.Builtin{ }, }, + // Example: + // Rye-itself//History? 10 ; last 10 lines (when running under REPL) "Rye-itself//History?": { Argsn: 2, Doc: "Returns a block of the last N lines from REPL history.", @@ -333,6 +358,9 @@ var builtins_baseio = map[string]*env.Builtin{ // stdout capture // ------------------------------------------------------------------------- + // Tests: + // equal { capture-stdout { print 1 } } "1\n" + // error { capture-stdout 1 } "capture-stdout": { // ** Argsn: 1, Doc: "Executes a block of code while capturing all output to stdout, returning the captured output as a string.", diff --git a/baseio/builtins_printing.go b/baseio/builtins_printing.go index 78d95bf1..e3fc0926 100644 --- a/baseio/builtins_printing.go +++ b/baseio/builtins_printing.go @@ -151,6 +151,12 @@ func DisplayRyeValue(ps *env.ProgramState, arg0 env.Object, interactive bool) (e // registered by evaldo.RegisterBaseBuiltins and do not require these deps. var builtins_printing_extra = map[string]*env.Builtin{ + // Example: + // display [1 2 3] + // Args: + // * value: Block, Dict, Table, TableRow, Markdown, or Error to display interactively + // Returns: + // * the selected value or the original value when user exits "display": { Pure: true, Argsn: 1, @@ -161,6 +167,12 @@ var builtins_printing_extra = map[string]*env.Builtin{ }, }, + // Example: + // _.. [1 2 3] + // Args: + // * value: Block, Dict, Table, TableRow, Markdown, or Error to display interactively + // Returns: + // * the selected value or the original value when user exits "_..": { Argsn: 1, Doc: "Shorthand alias for 'display' - interactively displays a value in the terminal with navigation capabilities.", @@ -222,6 +234,13 @@ var builtins_printing_extra = map[string]*env.Builtin{ }, }, + // Example: + // table { "n" } { 1 2 3 } |display\custom fn { row is-curr } { if is-curr > 0 { print "*" } print row } + // Args: + // * table: Table to display + // * renderer: Function called for each row with args (row is-current) + // Returns: + // * the selected row or original table when user exits "display\\custom": { Argsn: 2, Doc: "Interactively displays a Table in the terminal with a custom rendering function for each row.", @@ -252,6 +271,12 @@ var builtins_printing_extra = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { print\ssv { 1 2 "a" } } "1 2 a\n" + // Args: + // * values: Block to format as space-separated values + // Returns: + // * returns the input block after printing "print\\ssv": { Argsn: 1, Doc: "Prints a block of values as space-separated values followed by a newline, returning the input block.", @@ -266,6 +291,12 @@ var builtins_printing_extra = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { print\csv { 1 2 "a" } } "1,2,a\n" + // Args: + // * values: Block to format as comma-separated values + // Returns: + // * returns the input block after printing "print\\csv": { Argsn: 1, Doc: "Prints a block of values as comma-separated values followed by a newline, returning the input block.", diff --git a/batteries/builtins_bcrypt.go b/batteries/builtins_bcrypt.go index 760afce0..f095f359 100644 --- a/batteries/builtins_bcrypt.go +++ b/batteries/builtins_bcrypt.go @@ -73,6 +73,12 @@ var Builtins_bcrypt = map[string]*env.Builtin{ // * password: String to hash // Returns: // * string containing the bcrypt hash of the password + // Tests: + // equal { bcrypt-hash "secret" |type? } 'string + // Args: + // * password: String to hash + // Returns: + // * string containing the bcrypt hash of the password "bcrypt-hash": { Argsn: 1, Doc: "Generates a bcrypt hash from a password string.", @@ -87,6 +93,14 @@ var Builtins_bcrypt = map[string]*env.Builtin{ // * password: Plain text password to compare // Returns: // * integer 1 if the password matches the hash, 0 otherwise + // Tests: + // equal { bcrypt-check "$2a$10$N9qo8uLOickgx2ZMRZo4e.PFTlCOKQOLawQy5AdYyZuG7JC2rA9e." "password" } 1 + // equal { bcrypt-check "$2a$10$N9qo8uLOickgx2ZMRZo4e.PFTlCOKQOLawQy5AdYyZuG7JC2rA9e." "wrong" } 0 + // Args: + // * hash: String containing the bcrypt hash + // * password: Plain text password to compare + // Returns: + // * integer 1 if the password matches the hash, 0 otherwise "bcrypt-check": { Argsn: 2, Doc: "Compares a bcrypt hash with a plain text password.", @@ -100,6 +114,13 @@ var Builtins_bcrypt = map[string]*env.Builtin{ // * length: Integer number of random bytes to generate // Returns: // * string containing the hex-encoded random token + // Tests: + // equal { generate-token 4 |length? } 8 + // error { generate-token "x" } + // Args: + // * length: Integer number of random bytes to generate + // Returns: + // * string containing the hex-encoded random token "generate-token": { Argsn: 1, Doc: "Generates a cryptographically secure random token of the specified length in bytes.", diff --git a/batteries/builtins_http.go b/batteries/builtins_http.go index 036e0fd7..1e11184f 100644 --- a/batteries/builtins_http.go +++ b/batteries/builtins_http.go @@ -53,6 +53,10 @@ var Builtins_http = map[string]*env.Builtin{ // * native Go-server object that can handle HTTP requests "http-server": { Argsn: 1, + // Example: + // srv: http-server ":8080" + // srv .Handle "/" "Hello world!" + // ; srv .Serve ; blocks and serves requests Doc: "Creates a new HTTP server that listens on the specified address with a 10-second read header timeout.", Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { switch addr := arg0.(type) { diff --git a/batteries/builtins_json.go b/batteries/builtins_json.go index aa158615..dc0b1085 100644 --- a/batteries/builtins_json.go +++ b/batteries/builtins_json.go @@ -193,7 +193,7 @@ func BlockToJSONWithIdxs(block env.Block, idxs *env.Idxs) string { } bu.WriteString(RyeToJSONWithIdxs(val, idxs)) } - bu.WriteString("] ") + bu.WriteString("]") return bu.String() } @@ -212,7 +212,7 @@ func ListToJSONWithIdxs(list env.List, idxs *env.Idxs) string { } bu.WriteString(RyeToJSONWithIdxs(val, idxs)) } - bu.WriteString("] ") + bu.WriteString("]") return bu.String() } @@ -235,7 +235,7 @@ func DictToJSONWithIdxs(dict env.Dict, idxs *env.Idxs) string { bu.WriteString(RyeToJSONWithIdxs(val, idxs)) i = i + 1 } - bu.WriteString("} ") + bu.WriteString("}") return bu.String() } @@ -255,7 +255,7 @@ func ContextToJSON(ctx *env.RyeCtx, idxs *env.Idxs) string { bu.WriteString(RyeToJSONWithIdxs(val, idxs)) i = i + 1 } - bu.WriteString("} ") + bu.WriteString("}") return bu.String() } @@ -277,7 +277,7 @@ func TableRowToJSONWithIdxs(row env.TableRow, idxs *env.Idxs) string { bu.WriteString("\": ") bu.WriteString(RyeToJSONWithIdxs(val, idxs)) } - bu.WriteString("} ") + bu.WriteString("}") return bu.String() } diff --git a/batteries/builtins_markdown.go b/batteries/builtins_markdown.go index d11fa353..16d0a520 100644 --- a/batteries/builtins_markdown.go +++ b/batteries/builtins_markdown.go @@ -728,7 +728,7 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { for i, line := range wrappedLines { displayLines[i] = term.StrBold() + line + term.StrCloseProps() } - + item := MarkdownDisplayItem{ Type: "heading", Content: headingText, @@ -749,7 +749,7 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { linkLine := fmt.Sprintf("Link: %s -> %s", linkText, destination) // Wrap link if it's too long wrappedLines := wrapTextToWidth(linkLine, displayWidth) - + item := MarkdownDisplayItem{ Type: "link", Content: destination, @@ -770,13 +770,13 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { items = append(items, item) return ast.WalkContinue, nil } - + // Remove trailing newline for wrapping paragraph = strings.TrimRight(paragraph, "\n") - + // Wrap paragraph text to fit terminal width wrappedLines := wrapTextToWidth(paragraph, displayWidth) - + item := MarkdownDisplayItem{ Type: "paragraph", Content: paragraph, @@ -803,7 +803,7 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { } content = buf.String() } - + if content != "" { codeLines := strings.Split(strings.TrimRight(content, "\n"), "\n") displayLines := make([]string, len(codeLines)) @@ -814,7 +814,7 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { } displayLines[i] = " " + line // Indent code } - + item := MarkdownDisplayItem{ Type: "code", Content: content, @@ -827,7 +827,7 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { // Handle lists as complete blocks - collect all list items var listItems []string var listContent strings.Builder - + for c := node.FirstChild(); c != nil; c = c.NextSibling() { if listItem, ok := c.(*ast.ListItem); ok { var itemText bytes.Buffer @@ -840,7 +840,7 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { } } } - + if itemText.Len() > 0 { itemContent := itemText.String() listItems = append(listItems, itemContent) @@ -848,14 +848,14 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { } } } - + if len(listItems) > 0 { displayLines := make([]string, 0) for _, itemContent := range listItems { prefix := "• " itemWidth := displayWidth - len(prefix) wrappedLines := wrapTextToWidth(itemContent, itemWidth) - + for i, line := range wrappedLines { if i == 0 { displayLines = append(displayLines, prefix+line) @@ -864,7 +864,7 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { } } } - + item := MarkdownDisplayItem{ Type: "list", Content: strings.TrimRight(listContent.String(), "\n"), @@ -872,7 +872,7 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { } items = append(items, item) } - + return ast.WalkSkipChildren, nil case *ast.Blockquote: // Handle blockquotes @@ -886,18 +886,18 @@ func markdownDisplayItems(source string) []MarkdownDisplayItem { } } } - + if buf.Len() > 0 { quoteText := strings.TrimSpace(buf.String()) prefix := "> " quoteWidth := displayWidth - len(prefix) wrappedLines := wrapTextToWidth(quoteText, quoteWidth) - + displayLines := make([]string, len(wrappedLines)) for i, line := range wrappedLines { displayLines[i] = prefix + line } - + item := MarkdownDisplayItem{ Type: "quote", Content: quoteText, @@ -956,6 +956,13 @@ var Builtins_markdown = map[string]*env.Builtin{ }, // Convert markdown to HTML + // Tests: + // equal { markdown->html "# Title" |contains "html 123 } + // Args: + // * text: Markdown source string + // Returns: + // * string containing HTML "markdown->html": { Argsn: 1, Doc: "Converts Markdown text to HTML.", @@ -975,6 +982,13 @@ var Builtins_markdown = map[string]*env.Builtin{ }, // Create a new Markdown value from string + // Tests: + // equal { markdown "Hello" |type? } 'markdown + // ; equal { markdown markdown "Hello" |type? } 'markdown + // Args: + // * text: String or Markdown value + // Returns: + // * Markdown value "markdown": { Argsn: 1, Doc: "Creates a Markdown value from a string.", @@ -991,6 +1005,12 @@ var Builtins_markdown = map[string]*env.Builtin{ }, // Get raw markdown text from Markdown value + // Tests: + // equal { markdown "Hello" |markdown//text } "Hello" + // Args: + // * md: Markdown value + // Returns: + // * raw markdown text as string "markdown//text": { Argsn: 1, Doc: "Gets the raw markdown text from a Markdown value.", @@ -1004,6 +1024,12 @@ var Builtins_markdown = map[string]*env.Builtin{ }, // Get length of markdown text + // Tests: + // equal { markdown "Hello" |markdown//length } 5 + // Args: + // * md: Markdown value + // Returns: + // * integer length in characters "markdown//length": { Argsn: 1, Doc: "Gets the length of the markdown text in characters.", @@ -1017,6 +1043,12 @@ var Builtins_markdown = map[string]*env.Builtin{ }, // Convert Markdown value to HTML + // Tests: + // equal { markdown "# T" |markdown//to-html |contains " value.", + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + vars := os.Environ() + d := make(map[string]any, len(vars)) + for _, kv := range vars { + if eq := strings.IndexByte(kv, '='); eq >= 0 { + key := kv[:eq] + val := kv[eq+1:] + d[key] = *env.NewString(val) + } + } + return *env.NewDict(d) + }, + }, + + "process?": { + Argsn: 1, + Doc: "Gets detailed information about a specific process by PID (alias of process).", + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + switch pid := arg0.(type) { + case env.Integer: + proc, err := process.NewProcess(int32(pid.Value)) + if err != nil { + return evaldo.MakeBuiltinError(ps, err.Error(), "process?") + } + s := proccesTableBase() + processTableAdd(s, proc) + return s.Rows[0].ToDict() + default: + return *evaldo.MakeArgError(ps, 1, []env.Type{env.IntegerType}, "process?") + } + }, + }, + // Args: // * source: uri representing source directory // * destination: uri representing destination directory @@ -1216,20 +1333,9 @@ var Builtins_os = map[string]*env.Builtin{ // * dictionary containing detailed information about the specified process "process": { Argsn: 1, - Doc: "Gets detailed information about a specific process by PID.", + Doc: "Removed: use process? instead.", Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { - switch pid := arg0.(type) { - case env.Integer: - process, err := process.NewProcess(int32(pid.Value)) - if err != nil { - return evaldo.MakeBuiltinError(ps, err.Error(), "process") - } - s := proccesTableBase() - processTableAdd(s, process) - return s.Rows[0].ToDict() - default: - return *evaldo.MakeArgError(ps, 1, []env.Type{env.IntegerType}, "process") - } + return evaldo.MakeBuiltinError(ps, "This function was removed. Use process?.", "process") }, }, @@ -1364,7 +1470,16 @@ var Builtins_os = map[string]*env.Builtin{ // Tags: #archive #extract "un-tgz": { Argsn: 2, - Doc: "Extracts a .tar.gz archive to a directory.", + Doc: "Removed: use untgz instead.", + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + return evaldo.MakeBuiltinError(ps, "This function was removed. Use untgz.", "un-tgz") + }, + }, + + // Aliases for archive operations + "untgz": { + Argsn: 2, + Doc: "Extracts a .tar.gz archive to a directory (alias of un-tgz).", Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { switch src := arg0.(type) { case env.Uri: @@ -1372,16 +1487,61 @@ var Builtins_os = map[string]*env.Builtin{ case env.Uri: srcPath := resolvePath(ps.WorkingPath, src.GetPath()) dstPath := resolvePath(ps.WorkingPath, dst.GetPath()) - err := extractTarGz(srcPath, dstPath) - if err != nil { - return evaldo.MakeBuiltinError(ps, "Error extracting tar.gz: "+err.Error(), "un-tgz") + if err := extractTarGz(srcPath, dstPath); err != nil { + return evaldo.MakeBuiltinError(ps, "Error extracting tar.gz: "+err.Error(), "untgz") } return arg1 default: - return evaldo.MakeArgError(ps, 2, []env.Type{env.UriType}, "un-tgz") + return evaldo.MakeArgError(ps, 2, []env.Type{env.UriType}, "untgz") } default: - return evaldo.MakeArgError(ps, 1, []env.Type{env.UriType}, "un-tgz") + return evaldo.MakeArgError(ps, 1, []env.Type{env.UriType}, "untgz") + } + }, + }, + + "tar-gz": { + Argsn: 2, + Doc: "Creates a .tar.gz archive from a file or directory (alias of tgz).", + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + switch src := arg0.(type) { + case env.Uri: + switch dst := arg1.(type) { + case env.Uri: + srcPath := resolvePath(ps.WorkingPath, src.GetPath()) + dstPath := resolvePath(ps.WorkingPath, dst.GetPath()) + if err := createTarGz(srcPath, dstPath); err != nil { + return evaldo.MakeBuiltinError(ps, "Error creating tar.gz: "+err.Error(), "tar-gz") + } + return arg1 + default: + return evaldo.MakeArgError(ps, 2, []env.Type{env.UriType}, "tar-gz") + } + default: + return evaldo.MakeArgError(ps, 1, []env.Type{env.UriType}, "tar-gz") + } + }, + }, + + "untar-gz": { + Argsn: 2, + Doc: "Extracts a .tar.gz archive to a directory (alias of un-tgz).", + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + switch src := arg0.(type) { + case env.Uri: + switch dst := arg1.(type) { + case env.Uri: + srcPath := resolvePath(ps.WorkingPath, src.GetPath()) + dstPath := resolvePath(ps.WorkingPath, dst.GetPath()) + if err := extractTarGz(srcPath, dstPath); err != nil { + return evaldo.MakeBuiltinError(ps, "Error extracting tar.gz: "+err.Error(), "untar-gz") + } + return arg1 + default: + return evaldo.MakeArgError(ps, 2, []env.Type{env.UriType}, "untar-gz") + } + default: + return evaldo.MakeArgError(ps, 1, []env.Type{env.UriType}, "untar-gz") } }, }, @@ -1465,7 +1625,15 @@ var Builtins_os = map[string]*env.Builtin{ // Tags: #find #files "find": { Argsn: 1, - Doc: "Creates a new file finder starting from the given path(s).", + Doc: "Removed: use new-finder instead.", + Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { + return evaldo.MakeBuiltinError(ps, "This function was removed. Use new-finder.", "find") + }, + }, + + "new-finder": { + Argsn: 1, + Doc: "Creates a new file finder starting from the given path(s) (alias of find).", Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { switch path := arg0.(type) { case env.Uri: @@ -1481,13 +1649,13 @@ var Builtins_os = map[string]*env.Builtin{ case env.String: paths = append(paths, p.Value) default: - return evaldo.MakeBuiltinError(ps, "Block must contain only uris or strings", "find") + return evaldo.MakeBuiltinError(ps, "Block must contain only uris or strings", "new-finder") } } finder := find.NewFind(paths...) return *env.NewNative(ps.Idx, finder, "finder") default: - return evaldo.MakeArgError(ps, 1, []env.Type{env.UriType, env.BlockType}, "find") + return evaldo.MakeArgError(ps, 1, []env.Type{env.UriType, env.BlockType}, "new-finder") } }, }, diff --git a/batteries/builtins_pipes.go b/batteries/builtins_pipes.go index a8db462e..c2247b55 100644 --- a/batteries/builtins_pipes.go +++ b/batteries/builtins_pipes.go @@ -26,7 +26,8 @@ var Builtins_pipes = map[string]*env.Builtin{ // ##### Pipes ##### "Unix-like pipe operations for data processing" // - // Example: cat %file.txt |into-string + // Example: + // cat %./data/file.txt |into-string // Args: // * path: URI path to the file // Returns: @@ -45,7 +46,8 @@ var Builtins_pipes = map[string]*env.Builtin{ }, }, - // Example: find %/home/user |match ".go" |wcl + // Example: + // find %./data |wc\l // Args: // * path: URI path to the directory // Returns: @@ -1304,7 +1306,8 @@ var Builtins_pipes = map[string]*env.Builtin{ }, }, - // Example: cat %logfile.log |extract\regexp regexp `apitoken "([^"]+)` |into-block + // Example: + // echo "id=123 x id=45" |extract\regexp re "id=([0-9]+)" |into-block // Args: // * pipe: script-pipe to read from // * regexp: compiled regexp native; if the pattern has a capture group, group 1 is extracted, otherwise the whole match diff --git a/batteries/builtins_regexp.go b/batteries/builtins_regexp.go index 773304d4..b16cb090 100644 --- a/batteries/builtins_regexp.go +++ b/batteries/builtins_regexp.go @@ -37,6 +37,13 @@ var Builtins_regexp = map[string]*env.Builtin{ }, // short regexp constructor, mostly for console use like ls\ re "pr.?n.*" + // Tests: + // equal { re "[0-9]" |type? } 'native + // equal { re "[0-9]" |kind? } 'regexp + // Args: + // * pattern: String containing a regular expression pattern + // Returns: + // * native regexp object or error if pattern is invalid "re": { Argsn: 1, Doc: "Creates a compiled regular expression object from a pattern string.", diff --git a/evaldo/builtins_base_printing_not.go b/evaldo/builtins_base_printing_not.go index 799c4be0..fcae3d55 100644 --- a/evaldo/builtins_base_printing_not.go +++ b/evaldo/builtins_base_printing_not.go @@ -25,6 +25,12 @@ var builtins_printing = map[string]*env.Builtin{ // ##### Printing ##### "Functions for displaying and formatting values" // + // Tests: + // stdout { prns "A" } "A " + // Args: + // * value: Value to print + // Returns: + // * returns the input value "prns": { Argsn: 1, Doc: "Prints a value followed by a space, returning the input value.", @@ -39,6 +45,12 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { prn "A" } "A" + // Args: + // * value: Value to print + // Returns: + // * returns the input value "prn": { Argsn: 1, Doc: "Prints a value without adding a newline, returning the input value.", @@ -53,6 +65,12 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { print "A" } "A\n" + // Args: + // * value: Value to print + // Returns: + // * returns the input value "print": { Argsn: 1, Doc: "Prints a value followed by a newline, returning the input value.", @@ -67,6 +85,13 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { print2 "A" "B" } "A B\n" + // Args: + // * a: First value to print + // * b: Second value to print + // Returns: + // * returns the second input value "print2": { Argsn: 2, Doc: "Prints two values separated by a space and followed by a newline, returning the second value.", @@ -88,6 +113,13 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { prn2 "A" "B" } "A B" + // Args: + // * a: First value to print + // * b: Second value to print + // Returns: + // * returns the second input value "prn2": { Argsn: 2, Doc: "Prints two values separated by a space without a newline, returning the second value.", @@ -109,6 +141,13 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { prns2 "A" "B" } "A B " + // Args: + // * a: First value to print + // * b: Second value to print + // Returns: + // * returns the second input value "prns2": { Argsn: 2, Doc: "Prints two values each followed by a space, returning the second value.", @@ -129,6 +168,15 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // ; equal { format "x {} y {}" { 1 2 } } "x 1 y 2" + // ; error { format 1 { 1 2 } } + // ; error { format "{}" 42 } + // Args: + // * template: String with {} placeholders + // * values: Block of values to substitute into the template in order + // Returns: + // * string with placeholders replaced "format": { Argsn: 2, Doc: "Formats a block of values using a format string with {} placeholders, returning the resulting string.", @@ -151,6 +199,13 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // ; stdout { prnf 42 "Value={}" } "Value=42" + // Args: + // * value: Value to embed + // * template: String with {} placeholder + // Returns: + // * returns the input value after printing "prnf": { Argsn: 2, Doc: "Formats and prints a value by replacing {} in the template string, without a newline.", @@ -167,6 +222,13 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // ; stdout { printf 42 "Value={}" } "Value=42\n" + // Args: + // * value: Value to embed + // * template: String with {} placeholder + // Returns: + // * returns the input value after printing "printf": { Argsn: 2, Doc: "Formats and prints a value by replacing {} in the template string, followed by a newline.", @@ -183,6 +245,14 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // equal { embed 42 "v={}" } "v=42" + // error { 42 embed 123 } + // Args: + // * value: Value to embed + // * target: String or URI with {} placeholder + // Returns: + // * string or URI with placeholder replaced by the value "embed": { Argsn: 2, Doc: "Embeds a value into a string or URI by replacing {} placeholder with the string representation of the value.", @@ -202,6 +272,13 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { prnv 42 "v={}" } "v=42" + // Args: + // * value: Value to embed + // * template: String with {} placeholder + // Returns: + // * returns the input value after printing "prnv": { Argsn: 2, Doc: "Embeds a value into a string by replacing {} placeholder and prints it without a newline, returning the input value.", @@ -218,6 +295,13 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // stdout { printv 42 "v={}" } "v=42\n" + // Args: + // * value: Value to embed + // * template: String with {} placeholder + // Returns: + // * returns the input value after printing "printv": { Argsn: 2, Doc: "Embeds a value into a string by replacing {} placeholder and prints it followed by a newline, returning the input value.", @@ -265,6 +349,12 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // ; equal { 42 inspect |type? } 'string + // Args: + // * value: Value to inspect + // Returns: + // * string with diagnostic representation "inspect": { Argsn: 1, Doc: "Returns a string containing detailed type and value information about a value.", @@ -273,6 +363,12 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // equal { "A" esc } "\033A" + // Args: + // * str: Base string to prefix with escape character + // Returns: + // * string starting with escape character "esc": { Argsn: 1, Doc: "Creates an ANSI escape sequence by prepending the escape character (\\033) to the input string.", @@ -286,6 +382,13 @@ var builtins_printing = map[string]*env.Builtin{ }, }, + // Tests: + // equal { esc-val 42 "X={}" } "\033X=42" + // Args: + // * value: Value to embed + // * template: String with {} placeholder + // Returns: + // * string starting with escape character and embedded value "esc-val": { Argsn: 2, Doc: "Creates an ANSI escape sequence with an embedded value by replacing {} placeholder and prepending the escape character (\\033).", diff --git a/evaldo/builtins_base_types.go b/evaldo/builtins_base_types.go index d183e29c..77fcdef1 100644 --- a/evaldo/builtins_base_types.go +++ b/evaldo/builtins_base_types.go @@ -86,7 +86,8 @@ var builtins_types = map[string]*env.Builtin{ Doc: "Tries to turn a Rye value to string.", Pure: true, Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object { - return *env.NewString(arg0.Print(*ps.Idx)) + // Prefer Dump() to avoid display prefixes like leading "%" for file-uri + return *env.NewString(arg0.Dump(*ps.Idx)) }, }, diff --git a/tests/base.info.rye b/tests/base.info.rye index 8aa4883e..1b79369c 100644 --- a/tests/base.info.rye +++ b/tests/base.info.rye @@ -3441,4924 +3441,5063 @@ section "Collections " "" { } -section "Vector " "Vector operations" { - group "vector" - "Creates a vector from a block of numbers." +section "Types and Kinds " "" { + group "integer" + "Tries to change a Rye value (like string) to integer." { argsn 1 - arg `block: block of numbers to convert to a vector` - returns `vector object` + argtypes { + 1 [ String ] + } + arg `value: String or decimal value to convert to an integer` + returns `An integer value` } { - equal { vector [ 1 2 3 ] |type? } 'vector - equal { vector [ 1 2 3 ] |length? } 3 + equal { integer "1234" } 1234 + equal { integer 45.67 } 45 + ; equal { integer "123.4" } 123 + ; equal { integer "123.6" } 123 + ; equal { integer "123.4" } 123 + error { integer "abc" } } { } - group "normalize" - "Calculates the L2 norm (Euclidean length) of a vector." + group "decimal" + "Tries to change a Rye value (like string) to decimal." { argsn 1 - arg `vector: vector object to normalize` - returns `decimal representing the L2 norm (Euclidean length) of the vector` + argtypes { + 1 [ String ] + } + arg `value: String value to convert to a decimal` + returns `A decimal value` } { - equal { vector [ 3 4 ] |normalize } 5.0 + equal { decimal "123.4" } 123.4 + error { decimal "abc" } } { } - group "std-deviation" - "Calculates the standard deviation of a vector's elements." + group "string" + "Tries to turn a Rye value to string." { argsn 1 - arg `vector: vector object` - returns `decimal representing the standard deviation of the vector elements` + pure + arg `value: Any Rye value to convert to a string` + returns `A string representation of the value` } { - equal { cc math , vector [ 1 2 3 4 5 ] |std-deviation |round\to 2 } 1.58 + equal { string 'test } "test" + equal { string 123 } "123" + equal { string 123.4 } "123.400000" + equal { string "test" } "test" + equal { string true } "true" } { } - group "cosine-similarity" - "Calculates the cosine similarity between two vectors." + group "uri" + "Tries to change Rye value to an URI." { - argsn 2 - arg `vector1: first vector object` - arg `vector2: second vector object` - returns `decimal representing the cosine similarity between the two vectors` + argsn 1 + pure + argtypes { + 1 [ String ] + } } { - equal { cosine-similarity vector [ 1 0 ] vector [ 0 1 ] } 0.0 - equal { cosine-similarity vector [ 1 1 ] vector [ 1 1 ] } 1.0 + equal { uri "https://example.com" } https://example.com + ; error { uri "not-uri" } } { } - group "correlation" - "Calculates the correlation coefficient between two vectors." + group "file" + "Tries to change Rye value to a file." { - argsn 2 - arg `vector1: first vector object` - arg `vector2: second vector object` - returns `decimal representing the correlation coefficient between the two vectors` + argsn 1 + pure + argtypes { + 1 [ String ] + } } { - equal { correlation vector [ 1 2 3 4 5 ] vector [ 1 2 3 4 5 ] } 1.0 - equal { correlation vector [ 1 2 3 4 5 ] vector [ 5 4 3 2 1 ] } -1.0 + equal { file "example.txt" } %example.txt + equal { file 123 } %123 } { } - group "dot-product" - "Calculates the dot product between two vectors." + group "char" + "Tries to turn a Rye value (like integer) to ascii character." { - argsn 2 - arg `vector1: first vector object` - arg `vector2: second vector object` - returns `decimal representing the dot product of the two vectors` + argsn 1 + pure + argtypes { + 1 [ Integer ] + } + arg `value: Integer representing an ASCII code point` + returns `A string containing the character corresponding to the ASCII code` } { - equal { dot-product vector [ 1 2 3 ] vector [ 4 5 6 ] } 32.0 + equal { char 42 } "*" + error { char "*" } } { } - group "euclidean-distance" - "Calculates the Euclidean distance between two vectors. Useful for K-Means clustering." + group "ascii" + "Tries to turn a single character string to its ASCII code integer." { - argsn 2 - arg `vector1: first vector object` - arg `vector2: second vector object` - returns `decimal representing the Euclidean distance between the two vectors` + argsn 1 + pure + argtypes { + 1 [ String ] + } + arg `value: String containing a single character` + returns `An integer representing the ASCII code of the character` } { - equal { euclidean-distance vector [ 0 0 ] vector [ 3 4 ] } 5.0 - equal { euclidean-distance vector [ 1 2 3 ] vector [ 1 2 3 ] } 0.0 + equal { ascii "*" } 42 + equal { ascii "A" } 65 + error { ascii "ab" } + error { ascii 42 } } { } - group "mean-vectors" - "Calculates the element-wise mean of multiple vectors. Useful for creating 'Master Anchors'." + group "block" + "Turns a List, Vector, or TableRow to a Block" { argsn 1 - arg `block: block of vectors to average` - returns `new vector representing the element-wise mean of all input vectors` + pure + argtypes { + 1 [ List Vector TableRow ] + } + arg `value: List, Vector, or TableRow to convert to a block` + returns `A block containing the same elements as the input` } { - equal { mean-vectors [ vector [ 1 2 ] vector [ 3 4 ] ] |block } { 2.0 3.0 } + equal { list [ 1 2 3 ] |block |type? } 'block + equal { list [ 1 2 3 ] |block |first } 1 + equal { vector [ 1 2 3 ] |block } { 1.0 2.0 3.0 } + equal { table { 'a 'b } { 1 2 3 4 } |-> 0 |block } { 1 2 } } { } - group "unit-vector" - "Returns a new normalized vector (length = 1). Essential for consistent dot products and overlays." + group "word" + "Tries to change a Rye value to a word with same name." { argsn 1 - arg `vector: vector object to normalize` - returns `new vector with length 1 (unit vector)` + pure + argtypes { + 1 [ String Word Opword Pipeword Xword EXword ] + } + arg `value: String or word-like value to convert to a word` + returns `A word with the same name as the input value` } { - equal { vector [ 3 4 ] |unit-vector |normalize } 1.0 + equal { word "test" } 'test + error { word 123 } } { } - group "project-vector" - "Projects vector onto another vector. Answers: 'How much of this vector is in the direction of another?'" + group "is-string" + "Returns true if value is a string." { - argsn 2 - arg `vector1: vector to project` - arg `vector2: vector to project onto` - returns `new vector representing the projection of vector1 onto vector2` + argsn 1 + pure + arg `value: Any Rye value to test` + returns `Boolean true if the value is a string, false otherwise` } { - equal { vector [ 3 4 ] |project-vector vector [ 1 0 ] |block } { 3.0 0.0 } + equal { is-string "test" } true + equal { is-string 'test } false + equal { is-string 123 } false } { } - group "reject-vector" - "Removes projection from vector. Use this to 'subtract' a concept direction from a vector." + group "is-integer" + "Returns true if value is an integer." { - argsn 2 - arg `vector1: vector to reject from` - arg `vector2: vector representing the direction to remove` - returns `new vector with the projection onto vector2 removed` + argsn 1 + pure + arg `value: Any Rye value to test` + returns `Boolean true if the value is an integer, false otherwise` } { - equal { vector [ 3 4 ] |reject-vector vector [ 1 0 ] |block } { 0.0 4.0 } + equal { is-integer 123 } true + equal { is-integer 123.4 } false + equal { is-integer "123" } false } { } -} - -section "Matrix " "Matrix operations for 2D numerical data" { - group "matrix\\zeros" - "Creates a new matrix with the given dimensions, initialized to zeros." + group "is-decimal" + "Returns true if value is a decimal." { - argsn 2 - arg `rows: number of rows` - arg `cols: number of columns` - returns `a new zero-initialized matrix` + argsn 1 + pure + arg `value: Any Rye value to test` + returns `Boolean true if the value is a decimal, false otherwise` } { - equal { matrix\zeros 3 4 |type? } 'matrix - equal { matrix\zeros 2 3 |rows? } 2 - equal { matrix\zeros 2 3 |cols? } 3 + equal { is-decimal 123.4 } true + equal { is-decimal 123 } false + equal { is-decimal "123.4" } false } { } - group "matrix" - "Creates a matrix from a shape block and a data block." + group "is-number" + "Returns true if value is a number (integer or decimal)." { - argsn 2 - arg `shape: block with { rows cols }` - arg `data: block of decimal values in row-major order` - returns `a new matrix with the given data` + argsn 1 + pure + arg `value: Any Rye value to test` + returns `Boolean true if the value is a number (integer or decimal), false otherwise` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-get 1 2 } 6.0 + equal { is-number 123 } true + equal { is-number 123.4 } true + equal { is-number "123" } false } { } - group "matrix\\randn" - "Creates a matrix with random normal values (mean 0, std 1)." + group "type?" + "Returns the type of Rye value as a word." { - argsn 2 - arg `rows: number of rows` - arg `cols: number of columns` - returns `a new matrix with random normal values (mean 0, std 1)` + argsn 1 + pure + arg `value: Any Rye value to check the type of` + returns `A word representing the type of the value` } { - equal { matrix\randn 3 4 |rows? } 3 - equal { matrix\randn 3 4 |cols? } 4 + equal { type? "test" } 'string + equal { type? 123.4 } 'decimal } { } - group "matrix\\ones" - "Creates a matrix filled with ones." + group "kind?" + "Returns the kind of Rye value as a word." { - argsn 2 - arg `rows: number of rows` - arg `cols: number of columns` - returns `a new matrix filled with ones` + argsn 1 + pure + arg `value: Any Rye value to check the kind of` + returns `A word representing the kind of the value` } { - equal { matrix\ones 2 3 |mat-get 0 0 } 1.0 - equal { matrix\ones 2 3 |mat-get 1 2 } 1.0 + equal { kind? %file } 'file-uri } { } - group "matrix\\eye" - "Creates an identity matrix of size n×n." + group "types?" + "Returns the types of Rye values in a block or table row as a block of words." { argsn 1 - arg `n: size of the identity matrix` - returns `an n×n identity matrix` + pure + argtypes { + 1 [ Block Table TableRow ] + } + arg `collection: A block, table, or table row containing values to check types of` + returns `A block of words representing the types of each value in the collection` } { - equal { matrix\eye 3 |mat-get 0 0 } 1.0 - equal { matrix\eye 3 |mat-get 0 1 } 0.0 - equal { matrix\eye 3 |mat-get 1 1 } 1.0 + equal { types? { "test" 123 } } { string integer } } { } - group "shape?" - "Returns the shape of a matrix as { rows cols }." + group "dump" + "Returns (dumps) Rye code representing the object." { argsn 1 - arg `mat: matrix` - returns `block with { rows cols }` + pure + arg `value: Any Rye value to dump as code` + returns `A string containing the Rye code representation of the value` } { - equal { matrix\zeros 3 4 |shape? } { 3 4 } + equal { dump 123 } "123" + equal { dump "string" } `"string"` + equal { does { 1 } |dump } "fn { } { 1 }" } { } - group "rows?" - "Returns the number of rows in a matrix." + group "mold" + "Turn value to it's string representation." { argsn 1 - arg `mat: matrix` - returns `number of rows` + arg `value: Any Rye value to convert to a string representation` + returns `A string containing the representation of the value` } { - equal { matrix\zeros 3 4 |rows? } 3 + equal { mold 123 } "123" + equal { mold { 123 } } "{ 123 }" } { } - group "cols?" - "Returns the number of columns in a matrix." + group "mold\\nowrap" + "Turn value to it's string representation. Doesn't wrap the blocks" { argsn 1 - arg `mat: matrix` - returns `number of columns` + arg `value: Any Rye value to convert to a string representation` + returns `A string containing the representation of the value without block wrapping` } { - equal { matrix\zeros 3 4 |cols? } 4 + equal { mold\nowrap 123 } "123" + equal { mold\nowrap { 123 } } "123" + equal { mold\nowrap { 123 234 } } "123 234" } { } - group "mat-get" - "Gets the element at the specified row and column (0-indexed)." + group "assure-kind" + "Assuring kind." { - argsn 3 - arg `mat: matrix` - arg `row: row index (0-based)` - arg `col: column index (0-based)` - returns `the element at (row, col)` + argsn 2 + argtypes { + 1 [ Dict ] + 2 [ Kind ] + } + arg `value: Dict to convert to a specific kind` + arg `kind: Kind to convert the value to` + returns `A new context of the specified kind` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-get 0 1 } 2.0 - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-get 1 0 } 4.0 + equal { person: kind 'person { name: "" age: 0 } assure-kind dict { "name" "John" "age" 30 } person |type? } 'context } { } - group "mat-set!" - "Sets the element at the specified row and column (0-indexed). Modifies matrix in place." + group "method" + "Registers a method (generic function)." { - argsn 4 - arg `mat: matrix (modified in place)` - arg `row: row index (0-based)` - arg `col: column index (0-based)` - arg `val: value to set` - returns `the modified matrix` + argsn 3 + arg `kind: Word representing the kind for which to register the function` + arg `method: Word representing the method name` + arg `function: Function to register for the kind and method` + returns `The registered functionmethod fn [ a b ] [ a + b ] // tagwords are temporary here` } { - equal { m: matrix\zeros 2 2 , mat-set! m 0 1 5.0 , mat-get m 0 1 } 5.0 + equal { method 'integer 'add fn { a b } { a + b } |type? } 'function } { } - group "mat-row" - "Returns a row of the matrix as a vector." + group "kind" + "Creates new kind." { argsn 2 - arg `mat: matrix` - arg `row: row index (0-based)` - returns `the row as a vector` + argtypes { + 1 [ Word ] + 2 [ Block ] + } + arg `name: Word that will be the name of the kind` + arg `spec: Block containing the specification for the kind` + returns `A new kind object` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-row 0 |type? } 'vector + equal { kind 'person { name: "" age: 0 } |type? } 'kind } { } - group "mat-col" - "Returns a column of the matrix as a vector." + group "secret" + "Creates a secret from a string." { - argsn 2 - arg `mat: matrix` - arg `col: column index (0-based)` - returns `the column as a vector` + argsn 1 + pure + argtypes { + 1 [ String ] + } + arg `value: String value to convert to a secret` + returns `A secret value that hides the string from casual inspection` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-col 1 |type? } 'vector + equal { secret "password" |type? } 'secret + equal { secret "password" |reveal } "password" + error { secret 123 } } { } - group "mat-transpose" - "Returns the transpose of a matrix." + group "reveal" + "Reveals the secret value." { argsn 1 - arg `mat: matrix` - returns `transposed matrix` + pure + argtypes { + 1 [ Secret ] + } + arg `secret: Secret value to reveal` + returns `The string value contained in the secret` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-transpose |shape? } { 3 2 } - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-transpose |mat-get 0 1 } 4.0 + equal { secret "password" |reveal } "password" + equal { secret "test123" |reveal } "test123" + error { reveal "not-a-secret" } } { } - group "mat-mul" - "Matrix multiplication. A (m×n) × B (n×p) = C (m×p)." + group "lazy" + "Creates a lazy value from a block. The block will only be evaluated when forced with _!." { - argsn 2 - arg `A: left matrix (m×n)` - arg `B: right matrix (n×p)` - returns `result matrix (m×p)` + argsn 1 + argtypes { + 1 [ Block ] + } + arg `value: Dict or context to convert` + arg `kind: Kind to convert the value to` + returns `A new context of the specified kind` } { - equal { A: matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } , B: matrix { 3 2 } { 1.0 2.0 3.0 4.0 5.0 6.0 } , mat-mul A B |shape? } { 2 2 } - equal { A: matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } , B: matrix { 3 2 } { 1.0 2.0 3.0 4.0 5.0 6.0 } , mat-mul A B |mat-get 0 0 } 22.0 + ; equal { person: kind 'person { name: "" age: 0 } , { "name" "John" "age" 30 } .dict >> person |type? } 'ctx } { } - group "mat-hadamard" - "Element-wise (Hadamard) multiplication of two matrices. In the future, could have a dedicated .* operator." + group "_!" + "Forces evaluation of a lazy value. If frozen evaluates the block and caches the result. If already thawed returns cached result." { - argsn 2 - arg `A: first matrix` - arg `B: second matrix (same dimensions)` - returns `element-wise product` + argsn 1 + argtypes { + 1 [ LazyValue ] + } } { - equal { A: matrix\ones 2 2 , B: matrix\ones 2 2 , mat-hadamard A B |mat-get 0 0 } 1.0 } { } - group "mat-add" - "Element-wise addition of two matrices. In the future, could have a dedicated .+ operator." + group "_>>" + "Converts first argument to a specific kind." { argsn 2 - arg `A: first matrix` - arg `B: second matrix (same dimensions)` - returns `element-wise sum` + argtypes { + 1 [ Dict Kind ] + 2 [ Kind ] + } } { - equal { A: matrix\ones 2 2 , B: matrix\ones 2 2 , mat-add A B |mat-get 0 0 } 2.0 } { } - group "mat-sub" - "Element-wise subtraction of two matrices. In the future, could have a dedicated .- operator." +} + +section "Contexts " "Context related functions" { + group "raw-context" + "Creates a completely isolated context with no parent, where only built-in functions are available." { - argsn 2 - arg `A: first matrix` - arg `B: second matrix (same dimensions)` - returns `element-wise difference` + argsn 1 + argtypes { + 1 [ Block ] + } + arg `block: Block of expressions to evaluate in a new isolated context` + returns `context object with the values defined in the block` } { - equal { A: matrix\ones 2 2 , B: matrix\ones 2 2 , mat-sub A B |mat-get 0 0 } 0.0 + equal { c: raw-context { x: 123 } c/x } 123 + equal { y: 123 try { c: raw-context { x: y } } |type? } 'error ; word not found y + equal { try { c: raw-context { x: inc 10 } } |type? } 'error ; word not found inc } { } - group "mat-scale" - "Multiplies all elements of a matrix by a scalar." + group "isolate" + "Creates a context that can access the parent context during creation, but becomes isolated afterward." { - argsn 2 - arg `mat: matrix` - arg `scalar: number to multiply by` - returns `scaled matrix` + argsn 1 + argtypes { + 1 [ Block ] + } + arg `block: Block of expressions to evaluate in a temporary context` + returns `context object with the values defined in the block, but isolated from parent contexts` } { - equal { matrix\ones 2 2 |mat-scale 3.0 |mat-get 0 0 } 3.0 + equal { c: isolate { x: 123 } c/x } 123 + equal { y: 123 c: isolate { x: y } c/x } 123 + equal { c: isolate { x: inc 10 } c/x } 11 + ; equal { y: 99 c: isolate { x: does { y } } try { c/x } |type? } 'error + ; equal { y: 99 c: isolate { t: ?try x: does { t { y } } } c/x |type? } 'error } { } - group "mat-sum-rows" - "Returns a vector containing the sum of each row." + group "context" + "Creates a new context from a block (evaluated) or dict (converted)." { argsn 1 - arg `mat: matrix` - returns `vector with sum of each row` + argtypes { + 1 [ Block Dict ] + } + arg `arg: Block of expressions to evaluate, or Dict to convert to context` + returns `context object with the values defined in the block/dict and access to parent context` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-sum-rows |type? } 'vector + equal { c: context { x: 123 } c/x } 123 + equal { y: 123 c: context { x: y } c/x } 123 + equal { c: context { x: inc 10 } c/x } 11 + equal { y: 123 c: context { x: does { y } } c/x } 123 + equal { c: context { a: 1 b: 2 } c/a + c/b } 3 + equal { c: context dict { "a" 1 "b" 2 } c/a + c/b } 3 } { } - group "mat-sum-cols" - "Returns a vector containing the sum of each column." + group "context\\pure" + "Creates a new context using Pure Context (PCtx) as parent, preventing access to regular context changes." { argsn 1 - arg `mat: matrix` - returns `vector with sum of each column` + argtypes { + 1 [ Block ] + } + arg `block: Block of expressions to evaluate in a new pure context` + returns `context object with the values defined in the block, using Pure Context as parent` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-sum-cols |type? } 'vector - } - + equal { c: context\pure { x: 123 } c/x } 123 + ; error { y: 123 c: context\pure { x: y } } ; y not accessible in pure context + equal { c: context\pure { x: _+ 10 5 } c/x } 15 + } + { } - group "mat-max-rows" - "Returns a vector containing the maximum of each row." + group "private" + "Creates a temporary private context for evaluating expressions, returning the last value instead of the context." { argsn 1 - arg `mat: matrix` - returns `vector with max of each row` + argtypes { + 1 [ Block ] + } + arg `block: Block of expressions to evaluate in a private context` + returns `the last value from evaluating the block (not the context itself)` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-max-rows |type? } 'vector + equal { private { x: 123 } } 123 + equal { y: 123 private { x: y } } 123 + equal { private { x: inc 10 } } 11 + equal { y: 123 private { does { y } } :f f } 123 } { } - group "mat-max-cols" - "Returns a vector containing the maximum of each column." + group "private\\" + "Creates a documented private context for evaluating expressions, returning the last value instead of the context." { - argsn 1 - arg `mat: matrix` - returns `vector with max of each column` + argsn 2 + argtypes { + 1 [ String ] + 2 [ Block ] + } + arg `doc: String containing documentation for the context` + arg `block: Block of expressions to evaluate in a private context` + returns `the last value from evaluating the block (not the context itself)` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-max-cols |type? } 'vector + equal { private\ "what are we doing here" { x: 234 1000 + x } } 1234 } { } - group "mat-to-block" - "Converts a matrix to a block of blocks (each row is a block)." + group "extends" + "Creates a new context that inherits from a specified parent context." { - argsn 1 - arg `mat: matrix` - returns `block of blocks (each inner block is a row)` + argsn 2 + argtypes { + 1 [ Context ] + 2 [ Block ] + } + arg `parent: Context object to extend` + arg `block: Block of expressions to evaluate in the new context` + returns `new context object that inherits from the parent context` } { - equal { matrix { 2 2 } { 1.0 2.0 3.0 4.0 } |mat-to-block |length? } 2 + equal { ct: context { p: 123 } cn: extends ct { r: p + 234 } cn/r } 357 + ; error { ct: context { p: 123 } cn: extends ct { r: p + 234 } cn/r } } { } - group "block-to-mat" - "Converts a block of blocks (rows) to a matrix." + group "bind!" + "Binds a context as a parent context to first context." { - argsn 1 - arg `block: block of blocks (each inner block is a row)` - returns `matrix` + argsn 2 + argtypes { + 1 [ Context ] + 2 [ Context ] + } + arg `child: Context object to be bound` + arg `parent: Context object to bind to as parent` + returns `the modified child context with its parent set to the specified parent context` } { - equal { { { 1.0 2.0 } { 3.0 4.0 } } |block-to-mat |shape? } { 2 2 } + equal { c: context { y: 123 } cc: bind! context { z: does { y + 234 } } c , cc/z } 357 } { } - group "vector-to-mat" - "Converts a vector to a matrix. Use 'row for 1×n or 'col for n×1." + group "bind" + "Creates a clone of the first context and binds the second context as its parent." { argsn 2 - arg `vec: vector` - arg `orientation: 'row or 'col` - returns `matrix (1×n or n×1)` + argtypes { + 1 [ Context ] + 2 [ Context ] + } + arg `child: Context object to clone and bind` + arg `parent: Context object to bind to as parent` + returns `a cloned child context with its parent set to the specified parent context` } { - equal { vector { 1.0 2.0 3.0 } |vector-to-mat 'row |shape? } { 1 3 } - equal { vector { 1.0 2.0 3.0 } |vector-to-mat 'col |shape? } { 3 1 } + equal { c: context { x: 123 } p: context { y: 456 } cc: bind c p cc/x } 123 + equal { c: context { x: 123 } p: context { y: 456 } cc: bind c p cc/y } 456 + equal { c: context { x:: 123 } p: context { y:: 456 } cc: bind c p do\inside c { x:: 999 } cc/x } 123 ; clone unchanged + equal { c: context { x:: 123 } p: context { y:: 456 } cc: bind c p do\inside c { x:: 999 } c/x } 999 ; original modified } { } - group "mat-slice" - "Extracts a sub-matrix. Row and column ranges are { start end } (inclusive, 0-indexed)." + group "anchor" + "Creates a clone of a context and sets current context as its parent." { - argsn 3 - arg `mat: matrix` - arg `row-range: { start end } (inclusive)` - arg `col-range: { start end } (inclusive)` - returns `sub-matrix` + argsn 1 + argtypes { + 1 [ Context ] + } + arg `ctx: Context object to clone and anchor to current context` + returns `a cloned context with current context set as parent` } { - equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-slice { 0 1 } { 1 2 } |shape? } { 2 2 } + equal { c: context { x: 123 } cc: anchor c cc/x } 123 + equal { c: context { x:: 123 } cc: anchor c do\inside c { x:: 999 } cc/x } 123 ; clone unchanged + equal { c: context { x:: 123 } cc: anchor c do\inside c { x:: 999 } c/x } 999 ; original modified } { } - group "mat-softmax" - "Applies softmax to each column of the matrix. Returns a new matrix." + group "anchor!" + "Sets current context as parent of the given context (modifies the context in place)." { argsn 1 - arg `matrix: input matrix` - returns `a new matrix with softmax applied to each column` + argtypes { + 1 [ Context ] + } + arg `ctx: Context object to anchor to current context` + returns `the modified context with current context set as parent` } { - equal { matrix { 2 2 } { 1.0 2.0 3.0 4.0 } |mat-softmax |mat-get 0 0 |> 0.1 } true + equal { c: context { x: 123 } anchor! c c/x } 123 + equal { c: context { x:: 123 } anchor! c do\inside c { x:: 999 } c/x } 999 ; original modified } { } - group "mat-set-col!" - "Sets a column of the matrix from a vector or block. Mutates in place." + group "unbind" + "Removes the parent relationship from a context, making it a standalone context." { - argsn 3 - arg `matrix: matrix to modify` - arg `col: column index (0-based)` - arg `values: vector or block of values` - returns `the modified matrix` + argsn 1 + argtypes { + 1 [ Context ] + } + arg `ctx: Context object to unbind from its parent` + returns `the modified context with no parent` } { - equal { m: matrix\zeros 3 2 , mat-set-col! m 0 vector { 1.0 2.0 3.0 } , mat-get m 1 0 } 2.0 + equal { c: context { y: 123 } cc: bind! context { x: 234 z: does { x } } c , unbind cc cc/z } 234 + error { c: context { y: 123 } cc: bind! context { z: does { y } } c , dd: unbind cc dd/z } } { } - group "vec-add" - "Adds two vectors element-wise. Returns a new vector." + group "whereis" + "Searches for a word in current context and its parents, returns block with [ depth context ] where word was found." { - argsn 2 - arg `v1: first vector` - arg `v2: second vector` - returns `a new vector with element-wise sum` + argsn 1 + argtypes { + 1 [ Word ] + } + arg `word: Word to look up in current or parent contexts` + returns `block with [ depth context ] where depth is the number of parent hops (0 = current context) and context is where the word was found` } { - equal { vec-add vector { 1.0 2.0 } vector { 3.0 4.0 } |first } 4.0 + equal { x: 123 whereis 'x |first } 0 + equal { x: 123 c: context { y: 456 } do\in c { whereis 'x } |first } 0 + equal { x: 123 c: context { y: 456 } do\in c { whereis 'y } |first } 1 } { } - group "vec-sub" - "Subtracts two vectors element-wise (v1 - v2). Returns a new vector." + group "get" + "Gets the value of a word from current or parent contexts without calling it (similar to get-word ?word)." { - argsn 2 - arg `v1: first vector` - arg `v2: second vector` - returns `a new vector with element-wise difference (v1 - v2)` + argsn 1 + argtypes { + 1 [ Word ] + } + arg `word: Word to look up in current or parent contexts` + returns `the value bound to the word (including functions without calling them)` } { - equal { vec-sub vector { 5.0 3.0 } vector { 1.0 2.0 } |first } 4.0 + equal { x: 123 get 'x } 123 + equal { f: does { 101 } get 'f |type? } 'function + equal { c: context { y: 456 } do\in c { get 'y } } 456 } { } - group "vec-to-block" - "Converts a vector to a block of decimal values." + group "current" + "Returns current context." { - argsn 1 - arg `v: vector to convert` - returns `a block containing the vector values as decimals` } { - equal { vec-to-block vector { 1.0 2.0 3.0 } |second } 2.0 + equal { c: context { var 'x 9999 , incr: fn\inside { } current { x:: inc x } } c/incr c/x } 10000 } { } -} - -section "Types and Kinds " "" { - group "integer" - "Tries to change a Rye value (like string) to integer." + group "parent?" + "Returns parent context of the current context." { - argsn 1 - argtypes { - 1 [ String ] - } - arg `value: String or decimal value to convert to an integer` - returns `An integer value` } { - equal { integer "1234" } 1234 - equal { integer 45.67 } 45 - ; equal { integer "123.4" } 123 - ; equal { integer "123.6" } 123 - ; equal { integer "123.4" } 123 - error { integer "abc" } + equal { var 'y 99 c: context { incr: fn\inside { } parent? { y:: inc y } } c/incr y } 100 } { } - group "decimal" - "Tries to change a Rye value (like string) to decimal." + group "parent\\of" + "Returns parent context of the current context." { argsn 1 argtypes { - 1 [ String ] + 1 [ Context ] } - arg `value: String value to convert to a decimal` - returns `A decimal value` } { - equal { decimal "123.4" } 123.4 - error { decimal "abc" } + equal { ct: context { p: 123 } parent\of ct |= current } true } { } - group "string" - "Tries to turn a Rye value to string." + group "lc" + "Lists words in current context" { - argsn 1 pure - arg `value: Any Rye value to convert to a string` - returns `A string representation of the value` + arg `none` + returns `current context after printing the list of words to stdout` } { - equal { string 'test } "test" - equal { string 123 } "123" - equal { string 123.4 } "123.400000" - equal { string "test" } "test" - equal { string true } "true" } { } - group "uri" - "Tries to change Rye value to an URI." + group "lc\\data" + "Returns words in current context as a block." { - argsn 1 - pure - argtypes { - 1 [ String ] - } + arg `none` + returns `block of words defined in the current context` } { - equal { uri "https://example.com" } https://example.com - ; error { uri "not-uri" } + equal { x: 123 lc\data |type? } 'block } { } - group "file" - "Tries to change Rye value to a file." + group "lc\\data\\" + "Returns words in specified context as a block." { argsn 1 - pure argtypes { - 1 [ String ] + 1 [ Context ] } - } + arg `ctx: Context to list words from` + returns `block of words defined in the specified context` + } { - equal { file "example.txt" } %example.txt - equal { file 123 } %123 + equal { c: context { x: 123 } lc\data\ c |type? } 'block } { } - group "char" - "Tries to turn a Rye value (like integer) to ascii character." + group "lcp" + "Lists words in parent context" { - argsn 1 - pure - argtypes { - 1 [ Integer ] - } - arg `value: Integer representing an ASCII code point` - returns `A string containing the character corresponding to the ASCII code` + arg `none` + returns `current context after printing the parent context's words to stdout` } { - equal { char 42 } "*" - error { char "*" } } { } - group "ascii" - "Tries to turn a single character string to its ASCII code integer." + group "lc\\" + "Lists words in current context with string filter, by type (word: 'function, 'builtin, 'context), or regex filter (native of kind 'regexp)" { argsn 1 - pure argtypes { - 1 [ String ] + 1 [ String Word Native ] } - arg `value: String containing a single character` - returns `An integer representing the ASCII code of the character` + arg `filter: String to filter by name, Word ('function, 'builtin, 'context) to filter by type, or regexp Native` + returns `current context after printing filtered words to stdout` } { - equal { ascii "*" } 42 - equal { ascii "A" } 65 - error { ascii "ab" } - error { ascii 42 } } { } - group "block" - "Turns a List, Vector, or TableRow to a Block" + group "lcp\\" + "Lists words in parent context with string filter" { argsn 1 - pure argtypes { - 1 [ List Vector TableRow ] + 1 [ String ] } - arg `value: List, Vector, or TableRow to convert to a block` - returns `A block containing the same elements as the input` + arg `filter: String to filter word names` + returns `current context after printing filtered parent context words to stdout` } { - equal { list [ 1 2 3 ] |block |type? } 'block - equal { list [ 1 2 3 ] |block |first } 1 - equal { vector [ 1 2 3 ] |block } { 1.0 2.0 3.0 } - equal { table { 'a 'b } { 1 2 3 4 } |-> 0 |block } { 1 2 } } { } - group "word" - "Tries to change a Rye value to a word with same name." + group "cc" + "Change to context (pushes current context to stack for ccb)" { argsn 1 - pure argtypes { - 1 [ String Word Opword Pipeword Xword EXword ] + 1 [ Context ] } - arg `value: String or word-like value to convert to a word` - returns `A word with the same name as the input value` + arg `ctx: Context to change to` + returns `the new current context` } { - equal { word "test" } 'test - error { word 123 } } { } - group "is-string" - "Returns true if value is a string." + group "ccp" + "Change to parent context (pushes current context to stack for ccb)" { - argsn 1 - pure - arg `value: Any Rye value to test` - returns `Boolean true if the value is a string, false otherwise` + arg `none` + returns `the parent context (now current)` } { - equal { is-string "test" } true - equal { is-string 'test } false - equal { is-string 123 } false } { } - group "is-integer" - "Returns true if value is an integer." + group "ccb" + "Change context back (pops from context stack)" { - argsn 1 - pure - arg `value: Any Rye value to test` - returns `Boolean true if the value is an integer, false otherwise` + arg `none` + returns `the previous context (now current)` } { - equal { is-integer 123 } true - equal { is-integer 123.4 } false - equal { is-integer "123" } false } { } - group "is-decimal" - "Returns true if value is a decimal." + group "mkcc" + "Make context with current as parent and change to it (pushes current context to stack for ccb)." { argsn 1 - pure - arg `value: Any Rye value to test` - returns `Boolean true if the value is a decimal, false otherwise` + argtypes { + 1 [ Word ] + } + arg `word: Word to name the new context` + returns `the new context (now current)` } { - equal { is-decimal 123.4 } true - equal { is-decimal 123 } false - equal { is-decimal "123.4" } false } { } - group "is-number" - "Returns true if value is a number (integer or decimal)." + group "cc-stack-size" + "Returns the current size of the context navigation stack." { - argsn 1 - pure - arg `value: Any Rye value to test` - returns `Boolean true if the value is a number (integer or decimal), false otherwise` + arg `none` + returns `Integer with the current size of the context navigation stack` } { - equal { is-number 123 } true - equal { is-number 123.4 } true - equal { is-number "123" } false } { } - group "type?" - "Returns the type of Rye value as a word." + group "cc-clear-stack" + "Clears the context navigation stack (removes all stored previous contexts)." { - argsn 1 - pure - arg `value: Any Rye value to check the type of` - returns `A word representing the type of the value` + arg `none` + returns `Integer with the previous size of the stack before clearing` } { - equal { type? "test" } 'string - equal { type? 123.4 } 'decimal } { } - group "kind?" - "Returns the kind of Rye value as a word." + group "clone" + "Creates a copy of a context with the same state and parent relationship." { argsn 1 - pure - arg `value: Any Rye value to check the kind of` - returns `A word representing the kind of the value` + argtypes { + 1 [ Context ] + } + arg `ctx: Context object to clone` + returns `a new context object that is a copy of the original context` } { - equal { kind? %file } 'file-uri + equal { c: context { x: 123 y: 456 } cc: clone c cc/x } 123 + equal { c: context { x: 123 y: 456 } cc: clone c cc/y } 456 + equal { c: context { x:: 123 } cc: clone c do\inside cc { x:: 999 } c/x } 123 ; original unchanged + equal { c: context { x:: 123 } cc: clone c do\inside cc { x:: 999 } cc/x } 999 ; clone modified } { } - group "types?" - "Returns the types of Rye values in a block or table row as a block of words." + group "clone\\" + "Creates a copy of a context and evaluates a block of code inside the clone." { - argsn 1 - pure + argsn 2 argtypes { - 1 [ Block Table TableRow ] + 1 [ Context ] + 2 [ Block ] } - arg `collection: A block, table, or table row containing values to check types of` - returns `A block of words representing the types of each value in the collection` + arg `ctx: Context object to clone` + arg `block: Block of expressions to evaluate in the cloned context` + returns `the cloned context with the block evaluated inside it` } { - equal { types? { "test" 123 } } { string integer } + equal { c: context { x: 123 } cc: clone\ c { y: x + 100 } cc/y } 223 + ; error { c: context { x:: 123 } cc: clone\ c { y:: x + 100 } c/y } 'error ; y not in original context + equal { c: context { x:: 123 } cc: clone\ c { x:: 999 } c/x } 123 ; original unchanged + equal { c: context { x:: 123 } cc: clone\ c { x:: 999 } cc/x } 999 ; clone modified } { } - group "dump" - "Returns (dumps) Rye code representing the object." + group "clone\\deep" + "Creates a deep copy of a context with the same state and parent relationship, recursively copying all nested objects." { argsn 1 - pure - arg `value: Any Rye value to dump as code` - returns `A string containing the Rye code representation of the value` + argtypes { + 1 [ Context ] + } + arg `ctx: Context object to deep clone` + returns `a new context object that is a deep copy of the original context (including nested objects)` } { - equal { dump 123 } "123" - equal { dump "string" } `"string"` - equal { does { 1 } |dump } "fn { } { 1 }" + equal { c: context { x: [ 1 2 3 ] } cc: clone\deep c cc/x } [ 1 2 3 ] + equal { c: context { x: [ 1 2 3 ] } cc: clone\deep c do\in cc { change\nth! ref x 1 999 } c/x -> 0 } 1 ; original unchanged + equal { c: context { x: [ 1 2 3 ] } cc: clone\deep c do\in cc { change\nth! ref x 1 999 } cc/x -> 0 } 999 ; deep clone modified } { } - group "mold" - "Turn value to it's string representation." + group "call-depth?" + "Returns the current function call depth as an integer (0 = top-level, 1 = inside one function call, etc.)." { - argsn 1 - arg `value: Any Rye value to convert to a string representation` - returns `A string containing the representation of the value` + arg `(none)` + returns `current function call depth as integer (0 = top-level, 1 = inside one function, etc.)` } { - equal { mold 123 } "123" - equal { mold { 123 } } "{ 123 }" + equal { call-depth? } 0 + equal { test: fn { } { call-depth? } test } 1 } { } - group "mold\\nowrap" - "Turn value to it's string representation. Doesn't wrap the blocks" + group "max-call-depth!" + "Sets the maximum allowed function call depth (recursion limit). Use 0 for unlimited (default). Returns the previous limit. Calls that exceed this depth raise a stack-overflow error." { argsn 1 - arg `value: Any Rye value to convert to a string representation` - returns `A string containing the representation of the value without block wrapping` + argtypes { + 1 [ Integer ] + } + arg `limit: integer - new maximum call depth (0 = unlimited)` + returns `the previous MaxCallDepth value (0 = was unlimited)` } { - equal { mold\nowrap 123 } "123" - equal { mold\nowrap { 123 } } "123" - equal { mold\nowrap { 123 234 } } "123 234" + equal { max-call-depth! 10 , max-call-depth! 0 } 10 } { } - group "assure-kind" - "Assuring kind." + group "max-call-depth?" + "Returns the current maximum call depth limit as an integer (0 = unlimited)." { - argsn 2 - argtypes { - 1 [ Dict ] - 2 [ Kind ] - } - arg `value: Dict to convert to a specific kind` - arg `kind: Kind to convert the value to` - returns `A new context of the specified kind` + arg `(none)` + returns `current MaxCallDepth limit as integer (0 = unlimited)` } { - equal { person: kind 'person { name: "" age: 0 } assure-kind dict { "name" "John" "age" 30 } person |type? } 'context + equal { max-call-depth? } 0 + equal { max-call-depth! 50 max-call-depth? } 50 } { } - group "method" - "Registers a method (generic function)." + group "ops-done?" + "Returns the total number of expression evaluations performed so far in this program state as an integer." { - argsn 3 - arg `kind: Word representing the kind for which to register the function` - arg `method: Word representing the method name` - arg `function: Function to register for the kind and method` - returns `The registered functionmethod fn [ a b ] [ a + b ] // tagwords are temporary here` + arg `(none)` + returns `number of expression evaluations performed so far as integer` } { - equal { method 'integer 'add fn { a b } { a + b } |type? } 'function + equal { ops-done? > 0 } true } { } - group "kind" - "Creates new kind." + group "max-ops!" + "Sets the maximum number of expression evaluations allowed. Use 0 for unlimited (default). Returns the previous limit. Exceeding the limit raises an evaluation-limit error." { - argsn 2 + argsn 1 argtypes { - 1 [ Word ] - 2 [ Block ] + 1 [ Integer ] } - arg `name: Word that will be the name of the kind` - arg `spec: Block containing the specification for the kind` - returns `A new kind object` - } + arg `limit: integer - new maximum number of expression evaluations (0 = unlimited)` + returns `the previous MaxOps value (0 = was unlimited)` + } { - equal { kind 'person { name: "" age: 0 } |type? } 'kind + equal { max-ops! 100000 , max-ops! 0 } 100000 } { } - group "secret" - "Creates a secret from a string." + group "max-ops?" + "Returns the current maximum ops limit as an integer (0 = unlimited)." { - argsn 1 - pure - argtypes { - 1 [ String ] - } - arg `value: String value to convert to a secret` - returns `A secret value that hides the string from casual inspection` + arg `(none)` + returns `current MaxOps limit as integer (0 = unlimited)` } { - equal { secret "password" |type? } 'secret - equal { secret "password" |reveal } "password" - error { secret 123 } + equal { max-ops? } 0 + equal { max-ops! 50000 max-ops? } 50000 } { } - group "reveal" - "Reveals the secret value." + group "reset-ops" + "Resets the OpsCount counter to zero and clears the MaxOps limit. Returns the OpsCount value before reset. Useful for re-arming the budget inside a fresh computation phase." { - argsn 1 - pure - argtypes { - 1 [ Secret ] - } - arg `secret: Secret value to reveal` - returns `The string value contained in the secret` + arg `(none)` + returns `the OpsCount value before reset` } { - equal { secret "password" |reveal } "password" - equal { secret "test123" |reveal } "test123" - error { reveal "not-a-secret" } + equal { max-ops! 100 reset-ops max-ops? } 0 } { } - group "lazy" - "Creates a lazy value from a block. The block will only be evaluated when forced with _!." + group "enter" + "Combines with and do\\in: takes a value to inject, a context as parent, and a block to evaluate with both." { - argsn 1 + argsn 3 argtypes { - 1 [ Block ] + 2 [ Context ] + 3 [ Block ] } - arg `value: Dict or context to convert` - arg `kind: Kind to convert the value to` - returns `A new context of the specified kind` + arg `value: Value to inject into the block (like with)` + arg `context: Context to use as parent context during execution (like do\in)` + arg `block: Block of code to execute with both the injected value and specified parent context` + returns `result of executing the block with both the injected value and modified parent context` } { - ; equal { person: kind 'person { name: "" age: 0 } , { "name" "John" "age" 30 } .dict >> person |type? } 'ctx + equal { c: context { x: 100 } 123 |enter c { x } } 123 + equal { c: context { x: 100 } 123 |enter c { + x } } 223 + equal { pipes: context { echo: { .print } into-file: { .print } } 123 |enter pipes { .echo .into-file %data.txt } } 123 } { } - group "_!" - "Forces evaluation of a lazy value. If frozen evaluates the block and caches the result. If already thawed returns cached result." +} + +section "Flow control " "Functions for conditional execution and branching logic" { + group "if" + "Executes a block of code only if the condition is true, returning the result of the block or false." { - argsn 1 + argsn 2 + pure argtypes { - 1 [ LazyValue ] + 1 [ Boolean ] + 2 [ Block ] } + arg `condition: Boolean value determining whether to execute the block` + arg `block: Block of code to execute if condition is true` + returns `result of the block if condition is true, false otherwise` } { + equal { if true { 222 } } 222 + equal { if false { 333 } } false + error { if 1 { 222 } } + error { if 0 { 333 } } } { } - group "_>>" - "Converts first argument to a specific kind." + group "when" + "Conditionally executes an action block if a condition block evaluates to true, injecting the same value into both blocks and returning the value of the action block if the condition is truthy or the original value otherwise." { - argsn 2 + argsn 3 + pure argtypes { - 1 [ Dict Kind ] - 2 [ Kind ] + 2 [ Block ] + 3 [ Block ] } + arg `value: Value to inject into both condition and action blocks` + arg `condition: Block that evaluates to a truthy/falsy value with the injected value` + arg `action: Block to execute if condition is truthy, with the injected value` + returns `the value of the evaluated action block if condition is truthy or the original injected value if condition is falsy` } { + equal { 10 .when { > 5 } { + 3 } } 13 + equal { 10 .when { < 5 } { + 3 } } 10 } { } -} - -section "Contexts " "Context related functions" { - group "raw-context" - "Creates a completely isolated context with no parent, where only built-in functions are available." + group "^when" + "Conditionally executes an action block if a condition block evaluates to true, injecting the same value into both blocks and returning the value of the action block if the condition is truthy or the original value otherwise." { - argsn 1 + argsn 3 + pure argtypes { - 1 [ Block ] + 2 [ Block ] + 3 [ Block ] } - arg `block: Block of expressions to evaluate in a new isolated context` - returns `context object with the values defined in the block` + arg `value: Value to inject into both condition and action blocks` + arg `condition: Block that evaluates to a truthy/falsy value with the injected value` + arg `action: Block to execute if condition is truthy, with the injected value` + returns `the value of the evaluated action block if condition is truthy or the original injected value if condition is falsy` } { - equal { c: raw-context { x: 123 } c/x } 123 - equal { y: 123 try { c: raw-context { x: y } } |type? } 'error ; word not found y - equal { try { c: raw-context { x: inc 10 } } |type? } 'error ; word not found inc + equal { 10 .when { > 5 } { + 3 } } 13 + equal { 10 .when { < 5 } { + 3 } } 10 } { } - group "isolate" - "Creates a context that can access the parent context during creation, but becomes isolated afterward." + group "whether" + "Evaluates a condition block with the injected value and executes either the true or false block accordingly, returning the result. Like when with an else branch." { - argsn 1 + argsn 4 + pure argtypes { - 1 [ Block ] + 2 [ Block ] + 3 [ Block ] + 4 [ Block ] } - arg `block: Block of expressions to evaluate in a temporary context` - returns `context object with the values defined in the block, but isolated from parent contexts` + arg `value: Value to inject into the condition block and the chosen action block` + arg `condition: Block that evaluates to a truthy/falsy value with the injected value` + arg `true-block: Block to execute if condition is truthy, with the value injected` + arg `false-block: Block to execute if condition is falsy, with the value injected` + returns `result of the true-block if condition is truthy, result of the false-block otherwise` } { - equal { c: isolate { x: 123 } c/x } 123 - equal { y: 123 c: isolate { x: y } c/x } 123 - equal { c: isolate { x: inc 10 } c/x } 11 - ; equal { y: 99 c: isolate { x: does { y } } try { c/x } |type? } 'error - ; equal { y: 99 c: isolate { t: ?try x: does { t { y } } } c/x |type? } 'error + equal { 10 .whether { > 5 } { + 3 } { - 3 } } 13 + equal { 2 .whether { > 5 } { + 3 } { - 3 } } -1 + equal { 10 .whether { > 5 } { "big" } { "small" } } "big" + equal { 2 .whether { > 5 } { "big" } { "small" } } "small" } { } - group "context" - "Creates a new context from a block (evaluated) or dict (converted)." + group "^if" + "Conditional that sets the return flag when true, allowing early return from a function when the condition is a boolean." { - argsn 1 + argsn 2 + pure argtypes { - 1 [ Block Dict ] + 1 [ Boolean ] + 2 [ Block ] } - arg `arg: Block of expressions to evaluate, or Dict to convert to context` - returns `context object with the values defined in the block/dict and access to parent context` + arg `condition: Value to evaluate for truthiness` + arg `block: Block of code to execute and return from if condition is truthy` + returns `result of the block if condition is truthy (with return flag set), 0 otherwise` } { - equal { c: context { x: 123 } c/x } 123 - equal { y: 123 c: context { x: y } c/x } 123 - equal { c: context { x: inc 10 } c/x } 11 - equal { y: 123 c: context { x: does { y } } c/x } 123 - equal { c: context { a: 1 b: 2 } c/a + c/b } 3 - equal { c: context dict { "a" 1 "b" 2 } c/a + c/b } 3 + equal { x: does { ^if true { 222 } 555 } x } 222 + equal { x: does { ^if false { 333 } 444 } x } 444 } { } - group "context\\pure" - "Creates a new context using Pure Context (PCtx) as parent, preventing access to regular context changes." + group "either" + "Executes one of two blocks based on a boolean condition, similar to if/else in other languages." { - argsn 1 + argsn 3 + pure argtypes { - 1 [ Block ] + 1 [ Boolean ] + 3 [ Block ] } - arg `block: Block of expressions to evaluate in a new pure context` - returns `context object with the values defined in the block, using Pure Context as parent` + arg `condition: Boolean value determining which block to execute` + arg `true_block: Block or value to return if condition is true` + arg `false_block: Block or value to return if condition is false` + returns `result of executing the true_block if condition is true, otherwise result of false_block` } { - equal { c: context\pure { x: 123 } c/x } 123 - ; error { y: 123 c: context\pure { x: y } } ; y not accessible in pure context - equal { c: context\pure { x: _+ 10 5 } c/x } 15 + equal { either true { 222 } { 333 } } 222 + equal { either false { 222 } { 333 } } 333 + equal { either 5 > 3 { "yes" } { "no" } } "yes" + error { either 1 { 222 } { 333 } } + error { either 0 { 222 } { 333 } } } { } - group "private" - "Creates a temporary private context for evaluating expressions, returning the last value instead of the context." + group "choose" + "Returns the first or second value from a block of two values based on a boolean condition." { - argsn 1 + argsn 2 + pure argtypes { - 1 [ Block ] + 1 [ Boolean ] + 2 [ Block ] } - arg `block: Block of expressions to evaluate in a private context` - returns `the last value from evaluating the block (not the context itself)` + arg `condition: Boolean value determining which value to return` + arg `values: Block containing exactly two values` + returns `first value if condition is true, second value if condition is false` } { - equal { private { x: 123 } } 123 - equal { y: 123 private { x: y } } 123 - equal { private { x: inc 10 } } 11 - equal { y: 123 private { does { y } } :f f } 123 + equal { choose true [ 1 -1 ] } 1 + equal { choose false [ 1 -1 ] } -1 + equal { choose 10 > 5 [ "yes" "no" ] } "yes" + equal { choose 3 < 2 [ "yes" "no" ] } "no" + error { choose 1 [ 1 -1 ] } + error { choose true [ 1 ] } } { } - group "private\\" - "Creates a documented private context for evaluating expressions, returning the last value instead of the context." + group "switch" + "Pattern matching construct that executes a block of code corresponding to the first matching case value." { argsn 2 argtypes { - 1 [ String ] 2 [ Block ] } - arg `doc: String containing documentation for the context` - arg `block: Block of expressions to evaluate in a private context` - returns `the last value from evaluating the block (not the context itself)` + arg `value: Value to match against case values` + arg `cases: Block containing case values and corresponding handler blocks` + returns `result of executing the matching handler block, or the original value if no match` } { - equal { private\ "what are we doing here" { x: 234 1000 + x } } 1234 + equal { switch 101 { 101 { 111 } 202 { 222 } } } 111 + equal { switch 202 { 101 { 111 } 202 { 222 } } } 222 } { } - group "extends" - "Creates a new context that inherits from a specified parent context." + group "cases" + "Evaluates multiple condition-action pairs and applies all matching actions cumulatively to the initial value." { argsn 2 argtypes { - 1 [ Context ] 2 [ Block ] } - arg `parent: Context object to extend` - arg `block: Block of expressions to evaluate in the new context` - returns `new context object that inherits from the parent context` + arg `initial: Initial value to be transformed by matching case blocks` + arg `cases: Block containing condition blocks and corresponding transformation blocks` + returns `cumulative result after applying all matching transformation blocks to the initial value` } { - equal { ct: context { p: 123 } cn: extends ct { r: p + 234 } cn/r } 357 - ; error { ct: context { p: 123 } cn: extends ct { r: p + 234 } cn/r } + equal { cases 0 { { 1 > 0 } { + 100 } { 2 > 1 } { + 1000 } } } 1100 + equal { cases 0 { { 1 > 0 } { + 100 } { 2 < 1 } { + 1000 } } } 100 + equal { cases 0 { { 1 < 0 } { + 100 } { 2 > 1 } { + 1000 } } } 1000 + equal { cases 0 { { 1 < 0 } { + 100 } { 2 < 1 } { + 1000 } } } 0 + equal { cases 1 { { 1 > 0 } { + 100 } { 2 < 1 } { + 1000 } _ { * 3 } } } 101 + equal { cases 1 { { 1 < 0 } { + 100 } { 2 > 1 } { + 1000 } _ { * 3 } } } 1001 + equal { cases 1 { { 1 < 0 } { + 100 } { 2 < 1 } { + 1000 } _ { * 3 } } } 3 } { } - group "bind!" - "Binds a context as a parent context to first context." +} + +section "Iteration " "Functions for iterating over collections and executing code repeatedly" { + group "loop" + "Executes a block of code, builtin, or function a specified number of times, injecting the current iteration number (starting from 1)." { argsn 2 argtypes { - 1 [ Context ] - 2 [ Context ] + 1 [ Integer ] + 2 [ Block Builtin Function ] } - arg `child: Context object to be bound` - arg `parent: Context object to bind to as parent` - returns `the modified child context with its parent set to the specified parent context` + arg `count: Integer number of iterations to perform` + arg `block: Block of code to execute on each iteration` + returns `result of the last block execution` } { - equal { c: context { y: 123 } cc: bind! context { z: does { y + 234 } } c , cc/z } 357 + stdout { 3 .loop { prns "x" } } "x x x " + equal { 3 .loop { + 1 } } 3 + ; equal { 3 .loop { } } 3 ; TODO should pass the value } { } - group "bind" - "Creates a clone of the first context and binds the second context as its parent." + group "produce" + "Executes a block of code a specified number of times, passing the result of each execution to the next iteration." { - argsn 2 + argsn 3 argtypes { - 1 [ Context ] - 2 [ Context ] + 1 [ Integer ] + 3 [ Block ] } - arg `child: Context object to clone and bind` - arg `parent: Context object to bind to as parent` - returns `a cloned child context with its parent set to the specified parent context` + arg `count: Integer number of iterations to perform` + arg `initial: Initial value to inject into the first block execution` + arg `block: Block of code to execute on each iteration` + returns `result of the last block execution` } { - equal { c: context { x: 123 } p: context { y: 456 } cc: bind c p cc/x } 123 - equal { c: context { x: 123 } p: context { y: 456 } cc: bind c p cc/y } 456 - equal { c: context { x:: 123 } p: context { y:: 456 } cc: bind c p do\inside c { x:: 999 } cc/x } 123 ; clone unchanged - equal { c: context { x:: 123 } p: context { y:: 456 } cc: bind c p do\inside c { x:: 999 } c/x } 999 ; original modified + equal { produce 5 0 { + 3 } } 15 + equal { produce 3 ">" { ++ "x>" } } ">x>x>x>" + equal { produce 3 { } { .concat "x" } } { "x" "x" "x" } + equal { produce 3 { } { ::x .concat length? x } } { 0 1 2 } + equal { produce 5 { 2 } { ::acc .last ::x * x |concat* acc } } { 2 4 16 256 65536 4294967296 } } { } - group "anchor" - "Creates a clone of a context and sets current context as its parent." + group "replicate" + "Executes a block of code a specified number of times and collects all results into a block." { - argsn 1 + argsn 2 argtypes { - 1 [ Context ] + 1 [ Integer ] + 2 [ Block ] } - arg `ctx: Context object to clone and anchor to current context` - returns `a cloned context with current context set as parent` + arg `count: Integer number of times to execute the block` + arg `block: Block of code to execute on each iteration` + returns `Block containing the results of each execution` } { - equal { c: context { x: 123 } cc: anchor c cc/x } 123 - equal { c: context { x:: 123 } cc: anchor c do\inside c { x:: 999 } cc/x } 123 ; clone unchanged - equal { c: context { x:: 123 } cc: anchor c do\inside c { x:: 999 } c/x } 999 ; original modified + equal { replicate 5 { 10 } } { 10 10 10 10 10 } + equal { replicate 3 { 1 + 2 } } { 3 3 3 } + equal { replicate 0 { 10 } } { } } { } - group "anchor!" - "Sets current context as parent of the given context (modifies the context in place)." + group "replicate\\idx" + "Executes a block of code a specified number of times, injecting the current index (0-based), and collects all results into a block." { - argsn 1 + argsn 2 argtypes { - 1 [ Context ] + 1 [ Integer ] + 2 [ Block ] } - arg `ctx: Context object to anchor to current context` - returns `the modified context with current context set as parent` + arg `count: Integer number of times to execute the block` + arg `block: Block of code to execute on each iteration, with index (0-based) injected` + returns `Block containing the results of each execution` } { - equal { c: context { x: 123 } anchor! c c/x } 123 - equal { c: context { x:: 123 } anchor! c do\inside c { x:: 999 } c/x } 999 ; original modified + equal { replicate\idx 5 { + 0 } } { 0 1 2 3 4 } + equal { replicate\idx 3 { * 10 } } { 0 10 20 } + equal { replicate\idx 0 { 10 } } { } } { } - group "unbind" - "Removes the parent relationship from a context, making it a standalone context." + group "produce\\while" + "Executes a block of code repeatedly while a condition is true, passing the result of each execution to the next iteration." { - argsn 1 + argsn 3 argtypes { - 1 [ Context ] + 1 [ Integer ] + 3 [ Block ] } - arg `ctx: Context object to unbind from its parent` - returns `the modified context with no parent` + arg `condition: Block that evaluates to a boolean to determine when to stop iterating` + arg `initial: Initial value to inject into the first block execution` + arg `block: Block of code to execute on each iteration` + returns `result of the last block execution before the condition became false` } { - equal { c: context { y: 123 } cc: bind! context { x: 234 z: does { x } } c , unbind cc cc/z } 234 - error { c: context { y: 123 } cc: bind! context { z: does { y } } c , dd: unbind cc dd/z } + equal { var 'x 0 , produce\while { x < 100 } 1 { * 2 ::x } } 64 + stdout { var 'x 0 , produce\while { x < 100 } 1 { * 2 ::x .prns } } "2 4 8 16 32 64 128 " } { } - group "whereis" - "Searches for a word in current context and its parents, returns block with [ depth context ] where word was found." + group "produce\\" + "Executes a block of code a specified number of times, updating an accumulator word with each iteration result." { - argsn 1 + argsn 4 argtypes { - 1 [ Word ] + 1 [ Integer ] + 3 [ Word ] + 4 [ Block ] } - arg `word: Word to look up in current or parent contexts` - returns `block with [ depth context ] where depth is the number of parent hops (0 = current context) and context is where the word was found` + arg `count: Integer number of iterations to perform` + arg `initial: Initial value for the accumulator` + arg `accumulator: Word to store the accumulator value` + arg `block: Block of code to execute on each iteration` + returns `final value of the accumulator word after all iterations` } { - equal { x: 123 whereis 'x |first } 0 - equal { x: 123 c: context { y: 456 } do\in c { whereis 'x } |first } 0 - equal { x: 123 c: context { y: 456 } do\in c { whereis 'y } |first } 1 + equal { produce\ 5 1 'acc { * acc , + 1 } } 1 ; Look at what we were trying to do here } { } - group "get" - "Gets the value of a word from current or parent contexts without calling it (similar to get-word ?word)." + group "forever" + "Executes a block of code repeatedly until .return is called within the block." { argsn 1 argtypes { - 1 [ Word ] + 1 [ Block ] } - arg `word: Word to look up in current or parent contexts` - returns `the value bound to the word (including functions without calling them)` + arg `block: Block of code to execute repeatedly` + returns `result of the block when .return is called` } { - equal { x: 123 get 'x } 123 - equal { f: does { 101 } get 'f |type? } 'function - equal { c: context { y: 456 } do\in c { get 'y } } 456 + stdout { forever { "once" .prn .return } } "once" + equal { forever { "once" .return } } "once" } { } - group "current" - "Returns current context." + group "forever\\with" + "Accepts a value and a block, and executes the block repeatedly with the value until .return is called." { + argsn 2 + argtypes { + 1 [ Block ] + } + arg `value: Value to inject into the block on each iteration` + arg `block: Block of code to execute repeatedly` + returns `result of the block when .return is called` } { - equal { c: context { var 'x 9999 , incr: fn\inside { } current { x:: inc x } } c/incr c/x } 10000 + stdout { forever\with 1 { .prn .return } } "1" + equal { var 'x 0 , forever\with 1 { + 5 ::x , if x > 5 { return x } } } 6 } { } - group "parent?" - "Returns parent context of the current context." + group "for" + "Accepts a block of values and a block of code, builtin, or function, does the code for each of the values, injecting them." { + argsn 2 + argtypes { + 1 [ String Block Table ] + 2 [ Block Builtin Function ] + } + arg `collection: Collection (Block, List, String, or Table) to iterate over` + arg `code: Block, Builtin, or Function to execute for each value, injecting the value` + returns `result of the last code execution` } { - equal { var 'y 99 c: context { incr: fn\inside { } parent? { y:: inc y } } c/incr y } 100 + stdout { for { 1 2 3 } { prns "x" } } "x x x " + stdout { { "a" "b" "c" } .for { .prns } } "a b c " } { } - group "parent\\of" - "Returns parent context of the current context." + group "for\\pos" + "Iterates over a collection with position tracking (1-based), setting a word to the current position and injecting each value." { - argsn 1 + argsn 3 + pure argtypes { - 1 [ Context ] + 1 [ Block List String ] + 2 [ Word ] + 3 [ Block ] } + arg `collection: Collection to iterate over` + arg `word: Word to store the current position (1-based index)` + arg `code: Block of code to execute for each value` + returns `result of the last block execution` } { - equal { ct: context { p: 123 } parent\of ct |= current } true + stdout { { "a" "b" "c" } .for\pos 'i { i .prns , .prns } } "1 a 2 b 3 c " } { } - group "lc" - "Lists words in current context" + group "for\\idx" + "Iterates over a collection with index tracking (0-based), setting a word to the current index and injecting each value." { + argsn 3 pure - arg `none` - returns `current context after printing the list of words to stdout` + argtypes { + 1 [ Block List String ] + 2 [ Word ] + 3 [ Block ] + } + arg `collection: Collection to iterate over` + arg `word: Word to store the current index (0-based)` + arg `code: Block of code to execute for each value` + returns `result of the last block execution` } { + stdout { { "a" "b" "c" } .for\idx 'i { i .prns , .prns } } "0 a 1 b 2 c " } { } - group "lc\\data" - "Returns words in current context as a block." + group "walk" + "Walks through a block, executing code that can modify the block on each iteration (useful for custom iteration patterns)." { - arg `none` - returns `block of words defined in the current context` + argsn 2 + argtypes { + 1 [ Block ] + 2 [ Block ] + } + arg `block: Block to walk through` + arg `code: Block of code to execute, which should return a modified block` + returns `result of the last block execution` } { - equal { x: 123 lc\data |type? } 'block + stdout { walk { 1 2 3 } { .prns .rest } } "1 2 3 2 3 3 " + equal { var 'x 0 , walk { 1 2 3 } { ::b .first + x ::x , b .rest } x } 6 } { } - group "lc\\data\\" - "Returns words in specified context as a block." + group "walk\\pos" + "Walks through a block with position tracking (1-based), setting a word to the current position and executing code that can modify the block." { - argsn 1 + argsn 3 argtypes { - 1 [ Context ] + 1 [ Block ] + 2 [ Word ] + 3 [ Block ] } - arg `ctx: Context to list words from` - returns `block of words defined in the specified context` + arg `block: Block to walk through` + arg `word: Word to store the current position (1-based index)` + arg `code: Block of code to execute, which should return a modified block` + returns `result of the last block execution` } { - equal { c: context { x: 123 } lc\data\ c |type? } 'block + ; stdout { walk\pos { 1 2 3 } 'i { .prns , i .prns , .rest } } "{ 1 2 3 } 1 { 2 3 } 2 { 3 } 3 " } { } - group "lcp" - "Lists words in parent context" + group "walk\\idx" + "Walks through a block with index tracking (0-based), setting a word to the current index and executing code that can modify the block." { - arg `none` - returns `current context after printing the parent context's words to stdout` + argsn 3 + argtypes { + 1 [ Block ] + 2 [ Word ] + 3 [ Block ] + } + arg `block: Block to walk through` + arg `word: Word to store the current index (0-based)` + arg `code: Block of code to execute, which should return a modified block` + returns `result of the last block execution` } { + stdout { walk\idx { 1 2 3 } 'i { .prns , i .prns , .rest } } "1 2 3 0 2 3 1 3 2 " } { } - group "lc\\" - "Lists words in current context with string filter, by type (word: 'function, 'builtin, 'context), or regex filter (native of kind 'regexp)" + group "purge" + "Purges values from a series based on return of a injected code block." { - argsn 1 + argsn 2 argtypes { - 1 [ String Word Native ] + 1 [ Block List String Table ] + 2 [ Block ] } - arg `filter: String to filter by name, Word ('function, 'builtin, 'context) to filter by type, or regexp Native` - returns `current context after printing filtered words to stdout` + arg `series: Block, List, String, or Table to purge from` + arg `code: Block of code that returns true for values to remove` + returns `modified series with matching values removed` } { + equal { purge { 1 2 3 } { .is-even } } { 1 3 } + equal { purge { } { .is-even } } { } + equal { purge list { 1 2 3 } { .is-even } } list { 1 3 } + equal { purge list { } { .is-even } } list { } + equal { purge "1234" { .probe .integer .is-even } } { "1" "3" } + equal { purge "" { .integer .is-even } } { } } { } - group "lcp\\" - "Lists words in parent context with string filter" + group "purge!" + "Purges values from a block stored in a word (modifying it in-place), returning the purged values." { - argsn 1 + argsn 2 argtypes { - 1 [ String ] + 1 [ Block ] + 2 [ Word ] } - arg `filter: String to filter word names` - returns `current context after printing filtered parent context words to stdout` + arg `code: Block of code that returns true for values to remove` + arg `word: Word referring to a Block to purge from (modified in-place)` + returns `Block containing the purged values` } { + equal { { 1 2 3 } ::x purge! { .is-even } 'x , x } { 1 3 } } { } - group "cc" - "Change to context (pushes current context to stack for ccb)" + group "map" + "Maps values of a block to a new block by evaluating a block of code or function." { - argsn 1 + argsn 2 + pure argtypes { - 1 [ Context ] + 1 [ Block List String ] + 2 [ Block Builtin Function ] } - arg `ctx: Context to change to` - returns `the new current context` } { + equal { map { 1 2 3 } { + 1 } } { 2 3 4 } + equal { map { } { + 1 } } { } + equal { map { "aaa" "bb" "c" } { .length? } } { 3 2 1 } + equal { map list { "aaa" "bb" "c" } { .length? } } list { 3 2 1 } + equal { map list { 3 4 5 6 } { .is-multiple-of 3 } } list { 1 0 0 1 } + equal { map list { } { + 1 } } list { } + ; equal { map "abc" { + "-" } .join } "a-b-c-" ; TODO doesn't work, fix join + equal { map "123" { .integer } } { 1 2 3 } + equal { map "123" ?integer } { 1 2 3 } + equal { map "" { + "-" } } { } } { } - group "ccp" - "Change to parent context (pushes current context to stack for ccb)" + group "map\\pos" + "Maps values of a block to a new block by evaluating a block of code." { - arg `none` - returns `the parent context (now current)` + argsn 3 + pure + argtypes { + 1 [ Block List String ] + 2 [ Word ] + 3 [ Block ] + } } { + equal { map\pos { 1 2 3 } 'i { + i } } { 2 4 6 } + equal { map\pos { } 'i { + i } } { } + equal { map\pos list { 1 2 3 } 'i { + i } } list { 2 4 6 } + equal { map\pos list { } 'i { + i } } list { } + equal { map\pos "abc" 'i { ++ i } } { "a1" "b2" "c3" } + equal { map\pos "" 'i { + i } } { } } { } - group "ccb" - "Change context back (pops from context stack)" + group "map\\idx" + "Maps values of a block to a new block by evaluating a block of code." { - arg `none` - returns `the previous context (now current)` + argsn 3 + pure + argtypes { + 1 [ Block List String ] + 2 [ Word ] + 3 [ Block ] + } } { + equal { map\idx { 1 2 3 } 'i { + i } } { 1 3 5 } + equal { map\idx { } 'i { + i } } { } + equal { map\idx list { 1 2 3 } 'i { + i } } list { 1 3 5 } + equal { map\idx list { } 'i { + i } } list { } + equal { map\idx "abc" 'i { ++ i } } { "a0" "b1" "c2" } + equal { map\idx "" 'i { + i } } { } } { } - group "mkcc" - "Make context with current as parent and change to it (pushes current context to stack for ccb)." + group "reduce" + "Reduces values of a block to a single value by evaluating a block of code, using the first element as the initial accumulator." { - argsn 1 + argsn 3 + pure argtypes { - 1 [ Word ] + 1 [ Block List String ] + 2 [ Word ] + 3 [ Block ] } - arg `word: Word to name the new context` - returns `the new context (now current)` + arg `collection: Collection to reduce (must not be empty)` + arg `accumulator: Word to store the accumulator value` + arg `code: Block of code to execute for each value, receiving accumulator as context` + returns `final accumulated value` } { + equal { reduce { 1 2 3 } 'acc { + acc } } 6 + equal { reduce list { 1 2 3 } 'acc { + acc } } 6 + equal { reduce "abc" 'acc { ++ acc } } "cba" + equal { try { reduce { } 'acc { + acc } } |type? } 'error + equal { try { reduce list { } 'acc { + acc } } |type? } 'error + equal { try { reduce "" 'acc { + acc } } |type? } 'error + equal { reduce { 1 2 3 4 } 'acc { * acc } } 24 } { } - group "cc-stack-size" - "Returns the current size of the context navigation stack." + group "fold\\do" + "Reduces values of a block to a new block by evaluating a block of code ..." { - arg `none` - returns `Integer with the current size of the context navigation stack` + argsn 4 + pure + argtypes { + 1 [ Block List String ] + 2 [ Word ] + 4 [ Block ] + } } { + equal { fold\do { 1 2 3 } 'acc 1 { + acc } } 7 + equal { fold\do { } 'acc 1 { + acc } } 1 + equal { fold\do list { 1 2 3 } 'acc 1 { + acc } } 7 + equal { fold\do list { } 'acc 1 { + acc } } 1 + equal { fold\do "abc" 'acc "123" { ++ acc } } "cba123" + equal { fold\do "" 'acc "123" { + acc } } "123" } { } - group "cc-clear-stack" - "Clears the context navigation stack (removes all stored previous contexts)." + group "fold" + "Reduces values of a block to a new block by evaluating a block of code ..." { - arg `none` - returns `Integer with the previous size of the stack before clearing` + argsn 3 + pure + argtypes { + 1 [ Block List String ] + 4 [ Function ] + } } { + equal { fold { 1 2 3 } 1 fn { v acc } { + acc } } 7 + equal { fold { } 1 fn { v acc } { + acc } } 1 + equal { fold list { 1 2 3 } 1 fn { v acc } { v + acc } } 7 + equal { fold { "a" "b" "c" } "" fn { v acc } { acc .concat v } } "abc" } { } - group "clone" - "Creates a copy of a context with the same state and parent relationship." + group "partition" + "Partitions a series by evaluating a block of code, grouping consecutive elements that return the same result." { - argsn 1 + argsn 2 + pure argtypes { - 1 [ Context ] + 1 [ Block List String ] + 2 [ Block Builtin ] } - arg `ctx: Context object to clone` - returns `a new context object that is a copy of the original context` + arg `series: String, Block, or List to partition` + arg `code: Block or Builtin that returns a value for grouping consecutive elements` + returns `collection of partitions (each partition contains consecutive elements with the same result)` } { - equal { c: context { x: 123 y: 456 } cc: clone c cc/x } 123 - equal { c: context { x: 123 y: 456 } cc: clone c cc/y } 456 - equal { c: context { x:: 123 } cc: clone c do\inside cc { x:: 999 } c/x } 123 ; original unchanged - equal { c: context { x:: 123 } cc: clone c do\inside cc { x:: 999 } cc/x } 999 ; clone modified + equal { partition { 1 2 3 4 } { > 2 } } { { 1 2 } { 3 4 } } + equal { partition { "a" "b" 1 "c" "d" } { .is-integer } } { { "a" "b" } { 1 } { "c" "d" } } + equal { partition { "a" "b" 1 "c" "d" } ?is-integer } { { "a" "b" } { 1 } { "c" "d" } } + equal { partition { } { > 2 } } { { } } + equal { partition list { 1 2 3 4 } { > 2 } } list evals { list { 1 2 } list { 3 4 } } + equal { partition list { "a" "b" 1 "c" "d" } ?is-integer } list evals { list { "a" "b" } list { 1 } list { "c" "d" } } + equal { partition list { } { > 2 } } list evals { list { } } + equal { partition "aaabbccc" { , } } list { "aaa" "bb" "ccc" } + equal { partition "" { , } } list { "" } + equal { partition "aaabbccc" ?is-string } list { "aaabbccc" } + } + + { + } + + group "group" + "Groups a block or list of values by a key generated from a code block (keys must be strings)." + { + argsn 2 + pure + argtypes { + 1 [ Block List ] + 2 [ Block Builtin ] + } + arg `series: Block or List to group` + arg `code: Block or Builtin that returns a string key for grouping` + returns `Dict with string keys and List values containing grouped elements` + } + + { + ; Equality for dicts doesn't yet work consistently + ;equal { { "Anne" "Mitch" "Anya" } .group { .first } } dict evals { "A" list { "Anne" "Anya" } "M" list { "Mitch" } } + ;equal { { "Anne" "Mitch" "Anya" } .group ?first } dict evals { "A" list { "Anne" "Anya" } "M" list { "Mitch" } } + ;equal { { } .group { .first } } dict evals { } + ;equal { { "Anne" "Mitch" "Anya" } .list .group { .first } } dict evals { "A" list { "Anne" "Anya" } "M" list { "Mitch" } } + ;equal { { "Anne" "Mitch" "Anya" } .list .group ?first } dict evals { "A" list { "Anne" "Anya" } "M" list { "Mitch" } } + equal { { } .list .group { .first } } dict evals { } + equal { try { { 1 2 3 4 } .group { .is-even } } |type? } 'error ; TODO keys can only be string currently + } + + { + } + + group "filter" + "Filters values from a seris based on return of a injected code block." + { + argsn 2 + pure + argtypes { + 1 [ Block List String ] + 2 [ Block Builtin ] + } + } + + { + equal { filter { 1 2 3 4 } { .is-even } } { 2 4 } + equal { filter { 1 2 3 4 } ?is-even } { 2 4 } + equal { filter { } { .is-even } } { } + equal { filter list { 1 2 3 4 } { .is-even } } list { 2 4 } + equal { filter list { 1 2 3 4 } ?is-even } list { 2 4 } + equal { filter { 1 3 5 7 } { .is-even } } { } + ; equal { filter list { } { .is-even } } list { } + ; equal { filter "1234" { .integer .is-even } } { "2" "4" } + ; equal { filter "01234" ?integer } { "1" "2" "3" "4" } + ; equal { filter "" { .integer .is-even } } { } + } + + { + } + + group "seek" + "Seek over a series until a Block of code, Builtin, or Function returns True and return the value." + { + argsn 2 + pure + argtypes { + 1 [ Block List String ] + 2 [ Block Builtin Function ] + } + arg `series: Block, List, or String to search through` + arg `code: Block, Builtin, or Function that returns true when the desired value is found` + returns `first value for which the code returns true, error if none found` + } + + { + equal { seek { 1 2 3 4 } { .is-even } } 2 + equal { seek list { 1 2 3 4 } { .is-even } } 2 + equal { seek "1234" { .integer .is-even } } "2" + equal { try { seek { 1 2 3 4 } { > 5 } } |type? } 'error + equal { try { seek list { 1 2 3 4 } { > 5 } } |type? } 'error + equal { try { seek "1234" { .integer > 5 } } |type? } 'error + equal { seek { 1 2 3 4 } ?is-even } 2 + equal { seek list { 1 2 3 4 } ?is-even } 2 + equal { my-is-even: fn { x } { x .is-even } , seek { 1 2 3 4 } ?my-is-even } 2 + } + + { + } + + group "while" + "Executes a block of code repeatedly while a condition is true." + { + argsn 2 + argtypes { + 1 [ Block ] + 2 [ Block ] + } + arg `condition: Block that evaluates to a boolean to determine whether to continue` + arg `body: Block of code to execute repeatedly` + returns `result of the last body execution` + } + + { + equal { var 'x 0 while { x < 5 } { x:: x + 1 } x } 5 + equal { var 'x 0 var 'y 0 while { x < 5 } { x:: x + 1 y:: y + x } y } 15 + } + + { + } + + group "until" + "Executes a block of code repeatedly until a condition becomes true. Executes the body at least once before checking condition (do-until loop)." + { + argsn 2 + argtypes { + 1 [ Block ] + 2 [ Block ] + } + arg `body: Block of code to execute repeatedly (executed at least once)` + arg `condition: Block that evaluates to a boolean to determine when to stop` + returns `result of the last body execution` + } + + { + equal { var 'x 5 until { x = 0 } { x:: x - 1 } x } 0 + equal { var 'x 10 var 'y 0 until { x = 5 } { x:: x - 1 y:: y + x } y } 35 + equal { var 'x 3 until { x < 1 } { x:: x - 1 } x } 0 + equal { var 'x 0 until { x > 0 } { x:: x + 1 } x } 1 + } + + { + } + + group "for\\" + "Iterates over a collection, setting a word to each value. Works with lists, blocks, and strings." + { + argsn 3 + argtypes { + 1 [ Block List String ] + 2 [ Word ] + 3 [ Block ] + } + arg `collection: String, Block, List, or other Collection to iterate over` + arg `word: Word to store each value during iteration` + arg `block: Block of code to execute for each value` + returns `result of the last block execution` } { + stdout { { 1 2 3 } .for\ 'x { x .prns } } "1 2 3 " + stdout { list { 10 20 30 } |for\ 'val { val .prns } } "10 20 30 " + stdout { "abc" .for\ 'ch { ch .prns } } "a b c " } - group "clone\\" - "Creates a copy of a context and evaluates a block of code inside the clone." { - argsn 2 + } + + group "for\\kv" + "Iterates over a collection with separate key and value words. For dicts, uses actual keys and values. For other collections, uses indices as keys." + { + argsn 4 argtypes { - 1 [ Context ] - 2 [ Block ] + 1 [ Dict Block List String ] + 2 [ Word ] + 3 [ Word ] + 4 [ Block ] } - arg `ctx: Context object to clone` - arg `block: Block of expressions to evaluate in the cloned context` - returns `the cloned context with the block evaluated inside it` + arg `collection: Dict, Block, List, or other Collection to iterate over` + arg `keyWord: Word to store the key (or index for non-dict collections)` + arg `valueWord: Word to store the value` + arg `block: Block of code to execute for each key-value pair` + returns `result of the last block execution` } { - equal { c: context { x: 123 } cc: clone\ c { y: x + 100 } cc/y } 223 - ; error { c: context { x:: 123 } cc: clone\ c { y:: x + 100 } c/y } 'error ; y not in original context - equal { c: context { x:: 123 } cc: clone\ c { x:: 999 } c/x } 123 ; original unchanged - equal { c: context { x:: 123 } cc: clone\ c { x:: 999 } cc/x } 999 ; clone modified + stdout { dict { "a" 1 "b" 2 } |for\kv 'k 'v { k .prns v .prns } } "a 1 b 2 " + stdout { { 10 20 30 } .for\kv 'i 'v { i .prns v .prns } } "0 10 1 20 2 30 " } { } - group "clone\\deep" - "Creates a deep copy of a context with the same state and parent relationship, recursively copying all nested objects." +} + +section "Functions " "functions that create functions" { + group "var" + "Declares a word as a variable with the given value, allowing it to be modified. Returns the word for use with on-change." { - argsn 1 + argsn 2 argtypes { - 1 [ Context ] + 1 [ Tagword Word ] } - arg `ctx: Context object to deep clone` - returns `a new context object that is a deep copy of the original context (including nested objects)` + arg `word: Tagword representing the variable name` + arg `value: Initial value for the variable` + returns `The initial value` } { - equal { c: context { x: [ 1 2 3 ] } cc: clone\deep c cc/x } [ 1 2 3 ] - equal { c: context { x: [ 1 2 3 ] } cc: clone\deep c do\in cc { change\nth! ref x 1 999 } c/x -> 0 } 1 ; original unchanged - equal { c: context { x: [ 1 2 3 ] } cc: clone\deep c do\in cc { change\nth! ref x 1 999 } cc/x -> 0 } 999 ; deep clone modified + equal { var 'x 10 x:: 20 x } 20 } { } - group "call-depth?" - "Returns the current function call depth as an integer (0 = top-level, 1 = inside one function call, etc.)." + group "does" + "Creates a function with no arguments that executes the given block when called." { - arg `(none)` - returns `current function call depth as integer (0 = top-level, 1 = inside one function, etc.)` + argsn 1 + pure + argtypes { + 1 [ Block ] + } + arg `body: Block containing the function body code` + returns `function object with no parameters` } { - equal { call-depth? } 0 - equal { test: fn { } { call-depth? } test } 1 + equal { does { 123 } |type? } 'function + equal { x: does { 123 } x } 123 + equal { x: does { 1 + 2 } x } 3 } { } - group "max-call-depth!" - "Sets the maximum allowed function call depth (recursion limit). Use 0 for unlimited (default). Returns the previous limit. Calls that exceed this depth raise a stack-overflow error." + group "fn1" + "Creates a function that accepts one anonymous argument and executes the given block with that argument." { argsn 1 + pure argtypes { - 1 [ Integer ] + 1 [ Block ] } - arg `limit: integer - new maximum call depth (0 = unlimited)` - returns `the previous MaxCallDepth value (0 = was unlimited)` + arg `body: Block containing the function body code` + returns `function object that accepts one anonymous argument` } { - equal { max-call-depth! 10 , max-call-depth! 0 } 10 + equal { fn1 { .pass { } } |type? } 'function + equal { x: fn1 { } , x 123 } 123 + equal { x: fn1 { .pass { } } , x 123 } 123 + equal { x: fn1 { + 1 } , x 123 } 124 } { } - group "max-call-depth?" - "Returns the current maximum call depth limit as an integer (0 = unlimited)." + group "fn" + "Creates a function with named parameters specified in the first block and code in the second block." { - arg `(none)` - returns `current MaxCallDepth limit as integer (0 = unlimited)` + argsn 2 + pure + argtypes { + 1 [ Block ] + 2 [ Block ] + } + arg `spec: Block containing parameter specifications` + arg `body: Block containing the function body code` + returns `function object with the specified parameters` } { - equal { max-call-depth? } 0 - equal { max-call-depth! 50 max-call-depth? } 50 + equal { fn { } { } |type? } 'function + equal { x: fn { } { 234 } , x } 234 + equal { x: fn { x } { x } , x 123 } 123 + equal { x: fn { x } { + 123 } , x 123 } 246 + equal { add: fn { a b } { a + b } , add 10 20 } 30 } { } - group "ops-done?" - "Returns the total number of expression evaluations performed so far in this program state as an integer." + group "fn\\spec?" + "Returns the argument specification block of a function." { - arg `(none)` - returns `number of expression evaluations performed so far as integer` + argsn 1 + pure + argtypes { + 1 [ Function ] + } + arg `function: Function to get the argument spec from` + returns `Block containing the function's parameter specification` } { - equal { ops-done? > 0 } true + equal { f: fn { x y } { x + y } , ?f .fn\spec? } { x y } + equal { does { 1 } |fn\spec? } { } } { } - group "max-ops!" - "Sets the maximum number of expression evaluations allowed. Use 0 for unlimited (default). Returns the previous limit. Exceeding the limit raises an evaluation-limit error." + group "fn\\body?" + "Returns the body block of a function." { argsn 1 + pure argtypes { - 1 [ Integer ] + 1 [ Function ] } - arg `limit: integer - new maximum number of expression evaluations (0 = unlimited)` - returns `the previous MaxOps value (0 = was unlimited)` + arg `function: Function to get the body from` + returns `Block containing the function's body code` } { - equal { max-ops! 100000 , max-ops! 0 } 100000 + equal { f: fn { x y } { x + y } , ?f .fn\body? } { x + y } + equal { does { 123 } |fn\body? } { 123 } } { } - group "max-ops?" - "Returns the current maximum ops limit as an integer (0 = unlimited)." + group "pfn" + "Creates a pure function (no side effects allowed) with named parameters and code body." { - arg `(none)` - returns `current MaxOps limit as integer (0 = unlimited)` + argsn 2 + pure + argtypes { + 1 [ Block ] + 2 [ Block ] + } + arg `spec: Block containing parameter specifications` + arg `body: Block containing the function body code` + returns `pure function object with the specified parameters` } { - equal { max-ops? } 0 - equal { max-ops! 50000 max-ops? } 50000 + equal { pfn { } { } |type? } 'function + equal { x: pfn { x } { + 123 } , x 123 } 246 + ; TODO -- it seems pure namespace not also has print and append! error { x: pfn { } { ?append! } , x 123 } + ; TODO -- it seems pure namespace not also has print and append! error { x: pfn { x } { .print } , x 123 } } { } - group "reset-ops" - "Resets the OpsCount counter to zero and clears the MaxOps limit. Returns the OpsCount value before reset. Useful for re-arming the budget inside a fresh computation phase." + group "fn\\cc" + "Creates a function that captures the current context, allowing access to variables from the enclosing scope." { - arg `(none)` - returns `the OpsCount value before reset` + argsn 2 + pure + argtypes { + 1 [ Block ] + 2 [ Block ] + } + arg `spec: Block containing parameter specifications` + arg `body: Block containing the function body code` + returns `function object with the current context captured` } { - equal { max-ops! 100 reset-ops max-ops? } 0 + equal { fn\cc { x } { x + y } |type? } 'function + equal { y: 5 , f: fn\cc { x } { x + y } , f 3 } 8 } { } - group "enter" - "Combines with and do\\in: takes a value to inject, a context as parent, and a block to evaluate with both." + group "fn\\in" + "Creates a function with a specified parent context, allowing access to variables from that context." { argsn 3 + pure argtypes { + 1 [ Block ] 2 [ Context ] 3 [ Block ] } - arg `value: Value to inject into the block (like with)` - arg `context: Context to use as parent context during execution (like do\in)` - arg `block: Block of code to execute with both the injected value and specified parent context` - returns `result of executing the block with both the injected value and modified parent context` + arg `spec: Block containing parameter specifications` + arg `context: Context object to use as parent context` + arg `body: Block containing the function body code` + returns `function object with the specified parent context` } { - equal { c: context { x: 100 } 123 |enter c { x } } 123 - equal { c: context { x: 100 } 123 |enter c { + x } } 223 - equal { pipes: context { echo: { .print } into-file: { .print } } 123 |enter pipes { .echo .into-file %data.txt } } 123 + equal { ctx: context { y: 5 } , f: fn\in { x } ctx { x + y } , f 3 } 8 } { } -} - -section "Flow control " "Functions for conditional execution and branching logic" { - group "if" - "Executes a block of code only if the condition is true, returning the result of the block or false." + group "fn\\inside" + "Creates a function that executes directly in the specified context rather than creating a new execution context." { - argsn 2 + argsn 3 pure argtypes { - 1 [ Boolean ] - 2 [ Block ] + 1 [ Block ] + 2 [ Context ] + 3 [ Block ] } - arg `condition: Boolean value determining whether to execute the block` - arg `block: Block of code to execute if condition is true` - returns `result of the block if condition is true, false otherwise` + arg `spec: Block containing parameter specifications` + arg `context: Context object to execute the function in` + arg `body: Block containing the function body code` + returns `function object that executes directly in the specified context` } { - equal { if true { 222 } } 222 - equal { if false { 333 } } false - error { if 1 { 222 } } - error { if 0 { 333 } } + equal { ctx: context { y: 5 } , f: fn\inside { x } ctx { x + y } , f 3 } 8 } { } - group "when" - "Conditionally executes an action block if a condition block evaluates to true, injecting the same value into both blocks and returning the value of the action block if the condition is truthy or the original value otherwise." + group "modder" + "Creates a function that executes directly in the current context (like fn\\inside with current), allowing arguments and context words to be modified with word::." { - argsn 3 - pure + argsn 2 argtypes { + 1 [ Block ] 2 [ Block ] - 3 [ Block ] } - arg `value: Value to inject into both condition and action blocks` - arg `condition: Block that evaluates to a truthy/falsy value with the injected value` - arg `action: Block to execute if condition is truthy, with the injected value` - returns `the value of the evaluated action block if condition is truthy or the original injected value if condition is falsy` + arg `spec: Block containing parameter specifications` + arg `body: Block containing the function body code` + returns `function object that executes directly in the current context (captured at creation), with arguments as variables` } { - equal { 10 .when { > 5 } { + 3 } } 13 - equal { 10 .when { < 5 } { + 3 } } 10 + equal { var 'x 0 , inc: modder { } { x:: x + 1 } , inc , inc , x } 2 + equal { var 'n 10 , add: modder { v } { n:: n + v } , add 5 , n } 15 } { } - group "^when" - "Conditionally executes an action block if a condition block evaluates to true, injecting the same value into both blocks and returning the value of the action block if the condition is truthy or the original value otherwise." + group "closure" + "Creates a closure that captures the current context at creation time, preserving access to variables in that scope." { - argsn 3 + argsn 2 pure argtypes { + 1 [ Block ] 2 [ Block ] - 3 [ Block ] } - arg `value: Value to inject into both condition and action blocks` - arg `condition: Block that evaluates to a truthy/falsy value with the injected value` - arg `action: Block to execute if condition is truthy, with the injected value` - returns `the value of the evaluated action block if condition is truthy or the original injected value if condition is falsy` + arg `spec: Block containing parameter specifications` + arg `body: Block containing the function body code` + returns `function object that captures the current context at creation time` } { - equal { 10 .when { > 5 } { + 3 } } 13 - equal { 10 .when { < 5 } { + 3 } } 10 + equal { y: 5 , f: closure { x } { x + y } , f 3 } 8 + equal { mk-cntr: does { var 'c 0 , closure { } { inc! 'c } } cnt: mk-cntr , cnt + cnt + cnt } 6 } { } - group "whether" - "Evaluates a condition block with the injected value and executes either the true or false block accordingly, returning the result. Like when with an else branch." + group "partial" + "Creates a partially applied function with specified arguments, using _ (void) for arguments to be filled later." { - argsn 4 + argsn 2 pure argtypes { + 1 [ Builtin Function ] 2 [ Block ] - 3 [ Block ] - 4 [ Block ] } - arg `value: Value to inject into the condition block and the chosen action block` - arg `condition: Block that evaluates to a truthy/falsy value with the injected value` - arg `true-block: Block to execute if condition is truthy, with the value injected` - arg `false-block: Block to execute if condition is falsy, with the value injected` - returns `result of the true-block if condition is truthy, result of the false-block otherwise` + arg `func: Function or builtin to partially apply` + arg `args: Block of arguments, with _ (void) for arguments to be filled later` + returns `CurriedCaller object that can be called with the remaining arguments` } { - equal { 10 .whether { > 5 } { + 3 } { - 3 } } 13 - equal { 2 .whether { > 5 } { + 3 } { - 3 } } -1 - equal { 10 .whether { > 5 } { "big" } { "small" } } "big" - equal { 2 .whether { > 5 } { "big" } { "small" } } "small" + equal { prepend-star: partial ?concat [ "* " _ ] , prepend-star "hello" } "* hello" + equal { add-5: partial ?_+ [ _ 5 ] , add-5 10 } 15 + equal { fn-add: fn { x y } { x + y } , add-5: partial ?fn-add [ _ 5 ] , add-5 10 } 15 } { } - group "^if" - "Conditional that sets the return flag when true, allowing early return from a function when the condition is a boolean." + group "apply" + "Applies a function or builtin to a block of arguments." { argsn 2 - pure argtypes { - 1 [ Boolean ] + 1 [ Function Builtin VarBuiltin ] 2 [ Block ] } - arg `condition: Value to evaluate for truthiness` - arg `block: Block of code to execute and return from if condition is truthy` - returns `result of the block if condition is truthy (with return flag set), 0 otherwise` + arg `function: Function or builtin to apply` + arg `args: Block of arguments to pass to the function` + returns `The result of applying the function to the arguments` } { - equal { x: does { ^if true { 222 } 555 } x } 222 - equal { x: does { ^if false { 333 } 444 } x } 444 + equal { apply ?_+ { 12 23 } } 35 + equal { apply fn { x y } { x + y } { 5 10 } } 15 + equal { f: fn { x y } { x * y } , apply ?f { 7 6 } } 42 } { } - group "either" - "Executes one of two blocks based on a boolean condition, similar to if/else in other languages." + group "on-change" + "Registers an observer block to be executed when a variable changes. Use with 'var' result." { - argsn 3 - pure + argsn 2 argtypes { - 1 [ Boolean ] - 3 [ Block ] + 1 [ Word ] + 2 [ Block ] } - arg `condition: Boolean value determining which block to execute` - arg `true_block: Block or value to return if condition is true` - arg `false_block: Block or value to return if condition is false` - returns `result of executing the true_block if condition is true, otherwise result of false_block` + arg `word: Word representing the variable to observe` + arg `observer: Block containing code to execute when variable changes` + returns `The observer block` } { - equal { either true { 222 } { 333 } } 222 - equal { either false { 222 } { 333 } } 333 - equal { either 5 > 3 { "yes" } { "no" } } "yes" - error { either 1 { 222 } { 333 } } - error { either 0 { 222 } { 333 } } + equal { x: var 'counter 0 , on-change x { print "changed" } , counter:: 5 } 5 } { } - group "choose" - "Returns the first or second value from a block of two values based on a boolean condition." +} + +section "Other " "..." { + group "change!" + "Searches for a word and changes it's value in-place. Only works on variables declared with var. If value changes returns true otherwise false" { argsn 2 - pure argtypes { - 1 [ Boolean ] - 2 [ Block ] + 1 [ Word ] } - arg `condition: Boolean value determining which value to return` - arg `values: Block containing exactly two values` - returns `first value if condition is true, second value if condition is false` + arg `value: New value to assign to the word` + arg `word: Word whose value should be changed` + returns `Boolean true if the value changed, false if the new value is the same as the old value` } { - equal { choose true [ 1 -1 ] } 1 - equal { choose false [ 1 -1 ] } -1 - equal { choose 10 > 5 [ "yes" "no" ] } "yes" - equal { choose 3 < 2 [ "yes" "no" ] } "no" - error { choose 1 [ 1 -1 ] } - error { choose true [ 1 ] } + equal { var 'x 123 , change! 234 'x , x } 234 + equal { a:: 123 change! 333 'a a } 333 + equal { a:: 123 change! 124 'a } true + equal { a:: 123 change! 123 'a } false } { } - group "switch" - "Pattern matching construct that executes a block of code corresponding to the first matching case value." + group "modify!" + "Searches for a word and modifies it's value in-place. Only works on variables declared with var. If value changes returns true otherwise false" { argsn 2 argtypes { - 2 [ Block ] + 1 [ Word ] } - arg `value: Value to match against case values` - arg `cases: Block containing case values and corresponding handler blocks` - returns `result of executing the matching handler block, or the original value if no match` + arg `value: New value to assign to the word` + arg `word: Word whose value should be changed` + returns `Boolean true if the value changed, false if the new value is the same as the old value, will replace change!` } { - equal { switch 101 { 101 { 111 } 202 { 222 } } } 111 - equal { switch 202 { 101 { 111 } 202 { 222 } } } 222 + equal { var 'x 123 , modify! 234 'x , x } 234 + equal { a:: 123 modify! 333 'a a } 333 + equal { a:: 123 modify! 124 'a } true + equal { a:: 123 modify! 123 'a } false } { } - group "cases" - "Evaluates multiple condition-action pairs and applies all matching actions cumulatively to the initial value." + group "set" + "Set word to value or words by deconstructing a block. Only works on variables declared with var." { argsn 2 argtypes { + 1 [ Block ] 2 [ Block ] } - arg `initial: Initial value to be transformed by matching case blocks` - arg `cases: Block containing condition blocks and corresponding transformation blocks` - returns `cumulative result after applying all matching transformation blocks to the initial value` + arg `values: Value or block of values to assign to the word(s)` + arg `words: Word or block of words to be set` + returns `The value or block of values that was assigned` } { - equal { cases 0 { { 1 > 0 } { + 100 } { 2 > 1 } { + 1000 } } } 1100 - equal { cases 0 { { 1 > 0 } { + 100 } { 2 < 1 } { + 1000 } } } 100 - equal { cases 0 { { 1 < 0 } { + 100 } { 2 > 1 } { + 1000 } } } 1000 - equal { cases 0 { { 1 < 0 } { + 100 } { 2 < 1 } { + 1000 } } } 0 - equal { cases 1 { { 1 > 0 } { + 100 } { 2 < 1 } { + 1000 } _ { * 3 } } } 101 - equal { cases 1 { { 1 < 0 } { + 100 } { 2 > 1 } { + 1000 } _ { * 3 } } } 1001 - equal { cases 1 { { 1 < 0 } { + 100 } { 2 < 1 } { + 1000 } _ { * 3 } } } 3 + equal { set { 123 234 } { a b } b } 234 } { } -} - -section "Iteration " "Functions for iterating over collections and executing code repeatedly" { - group "loop" - "Executes a block of code, builtin, or function a specified number of times, injecting the current iteration number (starting from 1)." + group "unset!" + "Unset a word in current context, only meant to be used in console" { - argsn 2 + argsn 1 argtypes { - 1 [ Integer ] - 2 [ Block Builtin Function ] + 1 [ Word ] } - arg `count: Integer number of iterations to perform` - arg `block: Block of code to execute on each iteration` - returns `result of the last block execution` + arg `word: Word to be unset from the current context` + returns `Void value` } { - stdout { 3 .loop { prns "x" } } "x x x " - equal { 3 .loop { + 1 } } 3 - ; equal { 3 .loop { } } 3 ; TODO should pass the value + equal { x: 1 unset! 'x x: 2 } 2 ; otherwise would produce an error } { } - group "produce" - "Executes a block of code a specified number of times, passing the result of each execution to the next iteration." + group "val" + "Returns value of the word in context" { - argsn 3 + argsn 1 + pure argtypes { - 1 [ Integer ] - 3 [ Block ] + 1 [ Word ] } - arg `count: Integer number of iterations to perform` - arg `initial: Initial value to inject into the first block execution` - arg `block: Block of code to execute on each iteration` - returns `result of the last block execution` + arg `word: Word whose value should be retrieved` + returns `The value associated with the word in the current context` } { - equal { produce 5 0 { + 3 } } 15 - equal { produce 3 ">" { ++ "x>" } } ">x>x>x>" - equal { produce 3 { } { .concat "x" } } { "x" "x" "x" } - equal { produce 3 { } { ::x .concat length? x } } { 0 1 2 } - equal { produce 5 { 2 } { ::acc .last ::x * x |concat* acc } } { 2 4 16 256 65536 4294967296 } + equal { x: 123 val 'x } 123 + equal { x: 123 y: 'x val y } 123 } { } - group "replicate" - "Executes a block of code a specified number of times and collects all results into a block." + group "_<<" + "Converts a value to specific kind (R to L)" { argsn 2 argtypes { - 1 [ Integer ] - 2 [ Block ] + 1 [ Kind ] + 2 [ Dict Context ] } - arg `count: Integer number of times to execute the block` - arg `block: Block of code to execute on each iteration` - returns `Block containing the results of each execution` + arg `kind: Kind to convert the value to` + arg `value: Dict or context to convert` + returns `A new context of the specified kind` } { - equal { replicate 5 { 10 } } { 10 10 10 10 10 } - equal { replicate 3 { 1 + 2 } } { 3 3 3 } - equal { replicate 0 { 10 } } { } + ; TODO equal { person: kind 'person { name: "" age: 0 } person << dict { "name" "John" "age" 30 } |type? } 'ctx } { } - group "replicate\\idx" - "Executes a block of code a specified number of times, injecting the current index (0-based), and collects all results into a block." + group "_|" + "Pipeline operator that passes the value through unchanged (used with 'not' and other operations)." { - argsn 2 - argtypes { - 1 [ Integer ] - 2 [ Block ] - } - arg `count: Integer number of times to execute the block` - arg `block: Block of code to execute on each iteration, with index (0-based) injected` - returns `Block containing the results of each execution` + argsn 1 + pure + arg `value: Any value to be passed through unchanged` + returns `the original value (used in pipeline operations for explicit pass-through)` } { - equal { replicate\idx 5 { + 0 } } { 0 1 2 3 4 } - equal { replicate\idx 3 { * 10 } } { 0 10 20 } - equal { replicate\idx 0 { 10 } } { } + equal { 5 | } 5 + equal { "hello" | } "hello" + equal { true | } true + equal { { 1 2 3 } | } { 1 2 3 } } { } - group "produce\\while" - "Executes a block of code repeatedly while a condition is true, passing the result of each execution to the next iteration." + group "doc!" + "Sets docstring of the current context." { - argsn 3 + argsn 1 + pure argtypes { - 1 [ Integer ] - 3 [ Block ] + 1 [ String ] } - arg `condition: Block that evaluates to a boolean to determine when to stop iterating` - arg `initial: Initial value to inject into the first block execution` - arg `block: Block of code to execute on each iteration` - returns `result of the last block execution before the condition became false` + arg `doc: String to set as the docstring for the current context` + returns `Integer 1 on success` } { - equal { var 'x 0 , produce\while { x < 100 } 1 { * 2 ::x } } 64 - stdout { var 'x 0 , produce\while { x < 100 } 1 { * 2 ::x .prns } } "2 4 8 16 32 64 128 " + equal { x: private { doc! "some doc" doc? } } "some doc" } { } - group "produce\\" - "Executes a block of code a specified number of times, updating an accumulator word with each iteration result." + group "doc?" + "Gets docstring of the current context." { - argsn 4 - argtypes { - 1 [ Integer ] - 3 [ Word ] - 4 [ Block ] - } - arg `count: Integer number of iterations to perform` - arg `initial: Initial value for the accumulator` - arg `accumulator: Word to store the accumulator value` - arg `block: Block of code to execute on each iteration` - returns `final value of the accumulator word after all iterations` + pure + arg `None` + returns `String containing the docstring of the current context` } { - equal { produce\ 5 1 'acc { * acc , + 1 } } 1 ; Look at what we were trying to do here + equal { x: private { doc! "some doc" doc? } } "some doc" } { } - group "forever" - "Executes a block of code repeatedly until .return is called within the block." + group "doc\\of?" + "Get docstring of the passed context." { argsn 1 + pure argtypes { - 1 [ Block ] + 1 [ Context PersistentContext ] } - arg `block: Block of code to execute repeatedly` - returns `result of the block when .return is called` + arg `value: Function, builtin, or context to get the docstring from` + returns `String containing the docstring of the provided value` } { - stdout { forever { "once" .prn .return } } "once" - equal { forever { "once" .return } } "once" + equal { x: context { doc! "some doc" } doc\of? x } "some doc" } { } - group "forever\\with" - "Accepts a value and a block, and executes the block repeatedly with the value until .return is called." + group "ref" + "Makes a value mutable instead of immutable" { - argsn 2 + argsn 1 + pure argtypes { - 1 [ Block ] + 1 [ Table Dict List Block String Native Context ] } - arg `value: Value to inject into the block on each iteration` - arg `block: Block of code to execute repeatedly` - returns `result of the block when .return is called` + arg `value: Value to make mutable` + returns `A mutable reference to the value` } { - stdout { forever\with 1 { .prn .return } } "1" - equal { var 'x 0 , forever\with 1 { + 5 ::x , if x > 5 { return x } } } 6 + equal { is-ref ref { 1 2 3 } } true + equal { ref { 1 2 3 } |type? } 'block + equal { ref dict { "a" 1 } |type? } 'dict + equal { ref list { 1 2 3 } |type? } 'list + equal { ref "hello" |disarm |type? } 'string + ; TODO equal { b: ref { 1 2 3 } , append! b 4 , deref b |length? } 4 ; mutable in-place append } { } - group "for" - "Accepts a block of values and a block of code, builtin, or function, does the code for each of the values, injecting them." + group "deref" + "Makes a value again immutable" { - argsn 2 + argsn 1 + pure argtypes { - 1 [ String Block Table ] - 2 [ Block Builtin Function ] + 1 [ Table Dict List Block String Native Context ] } - arg `collection: Collection (Block, List, String, or Table) to iterate over` - arg `code: Block, Builtin, or Function to execute for each value, injecting the value` - returns `result of the last code execution` + arg `value: Mutable reference to make immutable` + returns `An immutable copy of the value` } { - stdout { for { 1 2 3 } { prns "x" } } "x x x " - stdout { { "a" "b" "c" } .for { .prns } } "a b c " + equal { is-ref deref ref { 1 2 3 } } false + equal { deref ref { 1 2 3 } } { 1 2 3 } + equal { deref ref "hello" } "hello" + equal { deref ref list { 1 2 3 } |length? } 3 + equal { ref dict { "a" 1 } |deref |type? } 'dict } { } - group "for\\pos" - "Iterates over a collection with position tracking (1-based), setting a word to the current position and injecting each value." + group "is-ref" + "Checks if a value is a mutable reference." { - argsn 3 - pure - argtypes { - 1 [ Block List String ] - 2 [ Word ] - 3 [ Block ] - } - arg `collection: Collection to iterate over` - arg `word: Word to store the current position (1-based index)` - arg `code: Block of code to execute for each value` - returns `result of the last block execution` + argsn 1 + arg `value: Any value to check if it's a reference` + returns `Integer 1 if the value is a reference, 0 otherwise` } { - stdout { { "a" "b" "c" } .for\pos 'i { i .prns , .prns } } "1 a 2 b 3 c " + equal { ref { } |is-ref } true + equal { { } |is-ref } false + equal { ref "hello" |is-ref } true + equal { "hello" |is-ref } false + equal { ref list { 1 2 3 } |is-ref } true + equal { 123 |is-ref } false } { } - group "for\\idx" - "Iterates over a collection with index tracking (0-based), setting a word to the current index and injecting each value." + group "is-same" + "Checks if two values are the same object (identity comparison, not value comparison)." { - argsn 3 + argsn 2 pure - argtypes { - 1 [ Block List String ] - 2 [ Word ] - 3 [ Block ] - } - arg `collection: Collection to iterate over` - arg `word: Word to store the current index (0-based)` - arg `code: Block of code to execute for each value` - returns `result of the last block execution` + arg `value1: First value to compare` + arg `value2: Second value to compare` + returns `Boolean true if values are the same object (identity), false otherwise` } { - stdout { { "a" "b" "c" } .for\idx 'i { i .prns , .prns } } "0 a 1 b 2 c " + equal { a: ref [ 1 2 ] is-same a a } true + equal { a: ref [ 1 2 ] b: ref [ 1 2 ] is-same a b } false + equal { a: ref [ 1 2 ] b: a is-same a b } true + equal { is-same 123 123 } true + equal { is-same "abc" "abc" } false ; strings are different objects } { } - group "walk" - "Walks through a block, executing code that can modify the block on each iteration (useful for custom iteration patterns)." + group "dict" + "Constructs a Dict from the Block of key and value pairs." { - argsn 2 - argtypes { - 1 [ Block ] - 2 [ Block ] - } - arg `block: Block to walk through` - arg `code: Block of code to execute, which should return a modified block` - returns `result of the last block execution` + argsn 1 + pure + arg `block: Block containing alternating keys and values` + returns `A new Dict with the specified keys and values` } { - stdout { walk { 1 2 3 } { .prns .rest } } "1 2 3 2 3 3 " - equal { var 'x 0 , walk { 1 2 3 } { ::b .first + x ::x , b .rest } x } 6 + equal { dict { "a" 123 } |-> "a" } 123 + equal { dict { "name" "John" "age" 30 } |-> "name" } "John" + equal { dict { } |type? } 'dict + equal { dict { "x" 1 "y" 2 "z" 3 } |length? } 3 + error { dict { 123 "value" } } ; integer keys should fail + error { dict { { } "value" } } ; block keys should fail + equal { dict { 'key "value" } |-> "key" } "value" ; tagwords as keys } { } - group "walk\\pos" - "Walks through a block with position tracking (1-based), setting a word to the current position and executing code that can modify the block." + group "change" + "Returns a new Dict with the specified key changed to the new value. Original dict is unchanged." { argsn 3 + pure argtypes { - 1 [ Block ] - 2 [ Word ] - 3 [ Block ] + 1 [ Dict ] + 2 [ String Word ] } - arg `block: Block to walk through` - arg `word: Word to store the current position (1-based index)` - arg `code: Block of code to execute, which should return a modified block` - returns `result of the last block execution` + arg `dict: Dict to update` + arg `key: String key to change` + arg `value: New value for the key` + returns `A new Dict with the key updated (original dict unchanged)` } { - ; stdout { walk\pos { 1 2 3 } 'i { .prns , i .prns , .rest } } "{ 1 2 3 } 1 { 2 3 } 2 { 3 } 3 " + equal { dict { "a" 1 "b" 2 } |change "a" 99 |-> "a" } 99 + equal { dict { "a" 1 "b" 2 } |change "c" 3 |-> "c" } 3 + equal { d: dict { "x" 10 } , change d "x" 20 |-> "x" } 20 } { } - group "walk\\idx" - "Walks through a block with index tracking (0-based), setting a word to the current index and executing code that can modify the block." + group "list" + "Constructs a List from the Block of values." { - argsn 3 - argtypes { - 1 [ Block ] - 2 [ Word ] - 3 [ Block ] - } - arg `block: Block to walk through` - arg `word: Word to store the current index (0-based)` - arg `code: Block of code to execute, which should return a modified block` - returns `result of the last block execution` + argsn 1 + pure + arg `block: Block containing values to put in the list` + returns `A new List with the values from the block` } { - stdout { walk\idx { 1 2 3 } 'i { .prns , i .prns , .rest } } "1 2 3 0 2 3 1 3 2 " + equal { list { "a" 123 } |-> 0 } "a" + equal { list { 1 2 3 } |length? } 3 + equal { list { } |length? } 0 + equal { list { "hello" "world" } |-> 1 } "world" + equal { list { 1 2 3 } |type? } 'list + equal { list { 1 2 3 } |first } 1 + equal { list { 1 2 3 } |last } 3 } { } - group "purge" - "Purges values from a series based on return of a injected code block." + group "load" + "Loads a string into Rye values." { - argsn 2 + argsn 1 argtypes { - 1 [ Block List String Table ] - 2 [ Block ] + 1 [ String ] } - arg `series: Block, List, String, or Table to purge from` - arg `code: Block of code that returns true for values to remove` - returns `modified series with matching values removed` + arg `source: String containing Rye code to load` + returns `Block containing the parsed Rye values` } { - equal { purge { 1 2 3 } { .is-even } } { 1 3 } - equal { purge { } { .is-even } } { } - equal { purge list { 1 2 3 } { .is-even } } list { 1 3 } - equal { purge list { } { .is-even } } list { } - equal { purge "1234" { .probe .integer .is-even } } { "1" "3" } - equal { purge "" { .integer .is-even } } { } + equal { load " 1 2 3 " |third } 3 + equal { load "{ 1 2 3 }" |first |third } 3 } { } - group "purge!" - "Purges values from a block stored in a word (modifying it in-place), returning the purged values." + group "load\\mod" + "Loads a string into Rye values. During load it allows modification of words." { - argsn 2 + argsn 1 argtypes { - 1 [ Block ] - 2 [ Word ] + 1 [ String ] } - arg `code: Block of code that returns true for values to remove` - arg `word: Word referring to a Block to purge from (modified in-place)` - returns `Block containing the purged values` + arg `source: String containing Rye code or URI of file to load with modification allowed` + returns `Block containing the parsed Rye valuesload\mod - string-only; file URI variant moved to builtins_baseio.go as "load\\mod\\file"` } { - equal { { 1 2 3 } ::x purge! { .is-even } 'x , x } { 1 3 } + ; load\mod file://modifiable.rye ; loads file with word modification allowed } { } - group "map" - "Maps values of a block to a new block by evaluating a block of code or function." + group "load\\live" + "Loads a string into Rye values. During load it allows modification of words." { - argsn 2 - pure + argsn 1 argtypes { - 1 [ Block List String ] - 2 [ Block Builtin Function ] + 1 [ String ] } + arg `source: String containing Rye code to load with modification allowed and file watching` + returns `Block containing the parsed Rye valuesload\live - string-only; file URI variant moved to builtins_baseio.go` } { - equal { map { 1 2 3 } { + 1 } } { 2 3 4 } - equal { map { } { + 1 } } { } - equal { map { "aaa" "bb" "c" } { .length? } } { 3 2 1 } - equal { map list { "aaa" "bb" "c" } { .length? } } list { 3 2 1 } - equal { map list { 3 4 5 6 } { .is-multiple-of 3 } } list { 1 0 0 1 } - equal { map list { } { + 1 } } list { } - ; equal { map "abc" { + "-" } .join } "a-b-c-" ; TODO doesn't work, fix join - equal { map "123" { .integer } } { 1 2 3 } - equal { map "123" ?integer } { 1 2 3 } - equal { map "" { + "-" } } { } + ; load\live file://watched.rye ; loads and watches file for changes } { } - group "map\\pos" - "Maps values of a block to a new block by evaluating a block of code." + group "load\\sig" + "Checks the signature, if OK then loads a string into Rye values." { - argsn 3 - pure - argtypes { - 1 [ Block List String ] - 2 [ Word ] - 3 [ Block ] - } + argsn 1 + arg `source: String containing signed Rye code to verify and load` + returns `Block containing the parsed Rye values if signature is valid` } { - equal { map\pos { 1 2 3 } 'i { + i } } { 2 4 6 } - equal { map\pos { } 'i { + i } } { } - equal { map\pos list { 1 2 3 } 'i { + i } } list { 2 4 6 } - equal { map\pos list { } 'i { + i } } list { } - equal { map\pos "abc" 'i { ++ i } } { "a1" "b2" "c3" } - equal { map\pos "" 'i { + i } } { } + ; load\sig "signed-code" ; loads only if signature is valid } { } - group "map\\idx" - "Maps values of a block to a new block by evaluating a block of code." + group "do" + "Takes a block of code and does (runs) it." { - argsn 3 + argsn 1 pure argtypes { - 1 [ Block List String ] - 2 [ Word ] - 3 [ Block ] + 1 [ Block ] } + arg `block: Block of code to execute` + returns `result of executing the block` } { - equal { map\idx { 1 2 3 } 'i { + i } } { 1 3 5 } - equal { map\idx { } 'i { + i } } { } - equal { map\idx list { 1 2 3 } 'i { + i } } list { 1 3 5 } - equal { map\idx list { } 'i { + i } } list { } - equal { map\idx "abc" 'i { ++ i } } { "a0" "b1" "c2" } - equal { map\idx "" 'i { + i } } { } + equal { do { 123 + 123 } } 246 + error { do { 123 + } } + equal { do { _+ _+ 12 23 34 } } 69 + equal { do { 12 * 23 |+ 34 } } 310 + equal { do { ( 12 * 23 ) + 34 } } 310 + equal { do { 12 * 23 | + 34 } } 310 + equal { do { 12 * 23 :a + 34 } } 310 + equal { do { 12 * 23 :a a + 34 } } 310 } { } - group "reduce" - "Reduces values of a block to a single value by evaluating a block of code, using the first element as the initial accumulator." + group "with" + "Takes a value and a block of code. It does the code with the value injected." { - argsn 3 - pure + argsn 2 argtypes { - 1 [ Block List String ] - 2 [ Word ] - 3 [ Block ] + 2 [ Block ] } - arg `collection: Collection to reduce (must not be empty)` - arg `accumulator: Word to store the accumulator value` - arg `code: Block of code to execute for each value, receiving accumulator as context` - returns `final accumulated value` + arg `value: Value to inject into the block's execution context` + arg `block: Block of code to execute with the injected value` + returns `result of executing the block with the injected value` } { - equal { reduce { 1 2 3 } 'acc { + acc } } 6 - equal { reduce list { 1 2 3 } 'acc { + acc } } 6 - equal { reduce "abc" 'acc { ++ acc } } "cba" - equal { try { reduce { } 'acc { + acc } } |type? } 'error - equal { try { reduce list { } 'acc { + acc } } |type? } 'error - equal { try { reduce "" 'acc { + acc } } |type? } 'error - equal { reduce { 1 2 3 4 } 'acc { * acc } } 24 + equal { with 100 { + 11 } } 111 + equal { with 100 { + 11 , * 3 } } 300 } { } - group "fold\\do" - "Reduces values of a block to a new block by evaluating a block of code ..." + group "do\\inside" + "Takes a Context and a Block. It Does a block inside a given Context." { - argsn 4 - pure + argsn 2 argtypes { - 1 [ Block List String ] - 2 [ Word ] - 4 [ Block ] + 1 [ Context PersistentContext ] + 2 [ Block ] } + arg `context: Context in which to execute the block` + arg `block: Block of code to execute within the specified context` + returns `result of executing the block within the given context` } { - equal { fold\do { 1 2 3 } 'acc 1 { + acc } } 7 - equal { fold\do { } 'acc 1 { + acc } } 1 - equal { fold\do list { 1 2 3 } 'acc 1 { + acc } } 7 - equal { fold\do list { } 'acc 1 { + acc } } 1 - equal { fold\do "abc" 'acc "123" { ++ acc } } "cba123" - equal { fold\do "" 'acc "123" { + acc } } "123" + equal { c: context { x: 100 } do\inside c { x * 9.99 } } 999.0 + equal { c: context { x:: 100 } do\inside c { inc! 'x } } 101 + equal { c: context { var 'x 100 } do\inside c { x:: 200 } c/x } 200 + equal { c: context { x:: 100 } do\inside c { x:: 200 , x } } 200 } { } - group "fold" - "Reduces values of a block to a new block by evaluating a block of code ..." + group "do\\in" + "Takes a Context and a Block. It Does a block in current context but with parent a given Context." { - argsn 3 - pure + argsn 2 argtypes { - 1 [ Block List String ] - 4 [ Function ] + 1 [ Context ] + 2 [ Block ] } + arg `context: Context to use as parent context during execution` + arg `block: Block of code to execute in current context with the specified parent context` + returns `result of executing the block with the modified parent context` } { - equal { fold { 1 2 3 } 1 fn { v acc } { + acc } } 7 - equal { fold { } 1 fn { v acc } { + acc } } 1 - equal { fold list { 1 2 3 } 1 fn { v acc } { v + acc } } 7 - equal { fold { "a" "b" "c" } "" fn { v acc } { acc .concat v } } "abc" + equal { c: context { x: 100 } do\in c { x * 9.99 } } 999.0 + equal { c: context { x:: 100 } do\in c { inc! 'x } } 101 + equal { c: context { x: 100 } do\in c { x:: 200 , x } } 200 + equal { c: context { x: 100 } do\in c { x:: 200 } c/x } 100 } { } - group "partition" - "Partitions a series by evaluating a block of code, grouping consecutive elements that return the same result." + group "do\\inx" + "Takes a Context and a Block. It Does a block in current context but with parent a given Context." { argsn 2 - pure argtypes { - 1 [ Block List String ] - 2 [ Block Builtin ] + 1 [ Context ] + 2 [ Block ] } - arg `series: String, Block, or List to partition` - arg `code: Block or Builtin that returns a value for grouping consecutive elements` - returns `collection of partitions (each partition contains consecutive elements with the same result)` + arg `context: Context to use as parent context during execution` + arg `block: Block of code to execute in current context with the specified parent context` + returns `result of executing the block with the modified parent context` } { - equal { partition { 1 2 3 4 } { > 2 } } { { 1 2 } { 3 4 } } - equal { partition { "a" "b" 1 "c" "d" } { .is-integer } } { { "a" "b" } { 1 } { "c" "d" } } - equal { partition { "a" "b" 1 "c" "d" } ?is-integer } { { "a" "b" } { 1 } { "c" "d" } } - equal { partition { } { > 2 } } { { } } - equal { partition list { 1 2 3 4 } { > 2 } } list evals { list { 1 2 } list { 3 4 } } - equal { partition list { "a" "b" 1 "c" "d" } ?is-integer } list evals { list { "a" "b" } list { 1 } list { "c" "d" } } - equal { partition list { } { > 2 } } list evals { list { } } - equal { partition "aaabbccc" { , } } list { "aaa" "bb" "ccc" } - equal { partition "" { , } } list { "" } - equal { partition "aaabbccc" ?is-string } list { "aaabbccc" } + equal { c: context { x: 100 } do\in c { x * 9.99 } } 999.0 + equal { c: context { x:: 100 } do\in c { inc! 'x } } 101 + equal { c: context { x: 100 } do\in c { x:: 200 , x } } 200 + equal { c: context { x: 100 } do\in c { x:: 200 } c/x } 100 } { } - group "group" - "Groups a block or list of values by a key generated from a code block (keys must be strings)." + group "lk\\" + "Lists available kinds with string filter" { - argsn 2 - pure + argsn 1 argtypes { - 1 [ Block List ] - 2 [ Block Builtin ] + 1 [ String ] } - arg `series: Block or List to group` - arg `code: Block or Builtin that returns a string key for grouping` - returns `Dict with string keys and List values containing grouped elements` } { - ; Equality for dicts doesn't yet work consistently - ;equal { { "Anne" "Mitch" "Anya" } .group { .first } } dict evals { "A" list { "Anne" "Anya" } "M" list { "Mitch" } } - ;equal { { "Anne" "Mitch" "Anya" } .group ?first } dict evals { "A" list { "Anne" "Anya" } "M" list { "Mitch" } } - ;equal { { } .group { .first } } dict evals { } - ;equal { { "Anne" "Mitch" "Anya" } .list .group { .first } } dict evals { "A" list { "Anne" "Anya" } "M" list { "Mitch" } } - ;equal { { "Anne" "Mitch" "Anya" } .list .group ?first } dict evals { "A" list { "Anne" "Anya" } "M" list { "Mitch" } } - equal { { } .list .group { .first } } dict evals { } - equal { try { { 1 2 3 4 } .group { .is-even } } |type? } 'error ; TODO keys can only be string currently } { } - group "filter" - "Filters values from a seris based on return of a injected code block." + group "lg\\" + "Lists generic words related to specific kind with string filter" { argsn 2 - pure argtypes { - 1 [ Block List String ] - 2 [ Block Builtin ] + 2 [ String ] } } { - equal { filter { 1 2 3 4 } { .is-even } } { 2 4 } - equal { filter { 1 2 3 4 } ?is-even } { 2 4 } - equal { filter { } { .is-even } } { } - equal { filter list { 1 2 3 4 } { .is-even } } list { 2 4 } - equal { filter list { 1 2 3 4 } ?is-even } list { 2 4 } - equal { filter { 1 3 5 7 } { .is-even } } { } - ; equal { filter list { } { .is-even } } list { } - ; equal { filter "1234" { .integer .is-even } } { "2" "4" } - ; equal { filter "01234" ?integer } { "1" "2" "3" "4" } - ; equal { filter "" { .integer .is-even } } { } } { } - group "seek" - "Seek over a series until a Block of code, Builtin, or Function returns True and return the value." + group "return" + "Accepts one value and returns it." { - argsn 2 - pure - argtypes { - 1 [ Block List String ] - 2 [ Block Builtin Function ] - } - arg `series: Block, List, or String to search through` - arg `code: Block, Builtin, or Function that returns true when the desired value is found` - returns `first value for which the code returns true, error if none found` + argsn 1 } { - equal { seek { 1 2 3 4 } { .is-even } } 2 - equal { seek list { 1 2 3 4 } { .is-even } } 2 - equal { seek "1234" { .integer .is-even } } "2" - equal { try { seek { 1 2 3 4 } { > 5 } } |type? } 'error - equal { try { seek list { 1 2 3 4 } { > 5 } } |type? } 'error - equal { try { seek "1234" { .integer > 5 } } |type? } 'error - equal { seek { 1 2 3 4 } ?is-even } 2 - equal { seek list { 1 2 3 4 } ?is-even } 2 - equal { my-is-even: fn { x } { x .is-even } , seek { 1 2 3 4 } ?my-is-even } 2 + equal { x: fn { } { return 101 202 } x } 101 } { } - group "while" - "Executes a block of code repeatedly while a condition is true." + group "rye" + "" { - argsn 2 - argtypes { - 1 [ Block ] - 2 [ Block ] - } - arg `condition: Block that evaluates to a boolean to determine whether to continue` - arg `body: Block of code to execute repeatedly` - returns `result of the last body execution` } { - equal { var 'x 0 while { x < 5 } { x:: x + 1 } x } 5 - equal { var 'x 0 var 'y 0 while { x < 5 } { x:: x + 1 y:: y + x } y } 15 + equal { rye .type? } 'native } { } - group "until" - "Executes a block of code repeatedly until a condition becomes true. Executes the body at least once before checking condition (do-until loop)." + group "Rye-itself//Capture" + "Registers a capture block that will be executed with the error injected if an unhandled error bubbles to the top of the program. The error value is injected as the first value (use .disarm, .print, etc.)." { argsn 2 argtypes { - 1 [ Block ] 2 [ Block ] } - arg `body: Block of code to execute repeatedly (executed at least once)` - arg `condition: Block that evaluates to a boolean to determine when to stop` - returns `result of the last body execution` + arg `rye-itself: The rye native object (passed via dot-word dispatch)` + arg `block: Block of code to execute with the captured error injected when an error bubbles to top-level` + returns `The rye-itself native (so the chain continues cleanly)` } { - equal { var 'x 5 until { x = 0 } { x:: x - 1 } x } 0 - equal { var 'x 10 var 'y 0 until { x = 5 } { x:: x - 1 y:: y + x } y } 35 - equal { var 'x 3 until { x < 1 } { x:: x - 1 } x } 0 - equal { var 'x 0 until { x > 0 } { x:: x + 1 } x } 1 + stdout { rye .Capture { .disarm .print } 1 / 0 } "Error: { message: division by zero }\n" + stdout { rye .Capture { .disarm .print } "ok" } "" } { } - group "for\\" - "Iterates over a collection, setting a word to each value. Works with lists, blocks, and strings." + group "defer" + "Registers a block of code to be executed when the current function exits or the program terminates." { - argsn 3 + argsn 1 argtypes { - 1 [ Block List String ] - 2 [ Word ] - 3 [ Block ] + 1 [ Block ] } - arg `collection: String, Block, List, or other Collection to iterate over` - arg `word: Word to store each value during iteration` - arg `block: Block of code to execute for each value` - returns `result of the last block execution` } { - stdout { { 1 2 3 } .for\ 'x { x .prns } } "1 2 3 " - stdout { list { 10 20 30 } |for\ 'val { val .prns } } "10 20 30 " - stdout { "abc" .for\ 'ch { ch .prns } } "a b c " + equal { x:: 123 defer { x:: 345 } x } 123 + stdout { ff:: fn { } { var 'x 123 defer { print 234 } x } , ff } "234\n" + equal { ff:: fn { } { x:: 123 defer { x:: 234 } x + 111 } , ff } 234 ; the result of defer expression is returned TODO, change this } { } - group "for\\kv" - "Iterates over a collection with separate key and value words. For dicts, uses actual keys and values. For other collections, uses indices as keys." + group "defer\\" + "Registers a block of code with an injected value to be executed when the current function exits or the program terminates. Works like 'with' but deferred." { - argsn 4 + argsn 2 argtypes { - 1 [ Dict Block List String ] - 2 [ Word ] - 3 [ Word ] - 4 [ Block ] + 2 [ Block ] } - arg `collection: Dict, Block, List, or other Collection to iterate over` - arg `keyWord: Word to store the key (or index for non-dict collections)` - arg `valueWord: Word to store the value` - arg `block: Block of code to execute for each key-value pair` - returns `result of the last block execution` + arg `value: Value to inject into the deferred block` + arg `block: Block to execute with the injected value when function exits` + returns `Void value` } { - stdout { dict { "a" 1 "b" 2 } |for\kv 'k 'v { k .prns v .prns } } "a 1 b 2 " - stdout { { 10 20 30 } .for\kv 'i 'v { i .prns v .prns } } "0 10 1 20 2 30 " + equal { x:: 0 defer\ 42 { + 1 } x } 0 + stdout { ff:: fn { } { defer\ "hello" { .print } "done" } , ff } "hello\n" } { } -} - -section "Functions " "functions that create functions" { - group "var" - "Declares a word as a variable with the given value, allowing it to be modified. Returns the word for use with on-change." + group "assert" + "Evaluates a block of code and asserts that its result equals the expected value. Returns the result if assertion passes, fails with error message if not." { argsn 2 argtypes { - 1 [ Tagword Word ] + 1 [ Block ] } - arg `word: Tagword representing the variable name` - arg `value: Initial value for the variable` - returns `The initial value` + arg `block: Block of code to evaluate` + arg `expected: Expected result value` + returns `The result value if assertion passes, error if it fails` } { - equal { var 'x 10 x:: 20 x } 20 + equal { assert { 1 + 2 } 3 } 3 + equal { assert { "hello" } "hello" } "hello" + error { assert { 1 + 2 } 5 } } { } - group "does" - "Creates a function with no arguments that executes the given block when called." + group "assert\\display" + "Evaluates a block of code and displays the result. Prints the explanation in magenta followed by OK in green on success, or error message in red on failure. Does not fail - continues evaluation." { - argsn 1 - pure + argsn 3 argtypes { - 1 [ Block ] + 1 [ String ] + 2 [ Block ] } - arg `body: Block containing the function body code` - returns `function object with no parameters` + arg `explanation: String describing the test being performed` + arg `block: Block of code to evaluate` + arg `expected: Expected result value` + returns `The result value (displays status but continues evaluation even on failure)` } { - equal { does { 123 } |type? } 'function - equal { x: does { 123 } x } 123 - equal { x: does { 1 + 2 } x } 3 + stdout { assert\display "check addition" { 1 + 2 } 3 } "\e[35mcheck addition\e[0m: \e[32mOK\e[0m\n" + ; stdout { assert\display "check addition" { 1 + 2 } 5 } "\e[35mcheck addition\e[0m: \e[31FAILED: expected `[Integer: 5]` but got `[Integer: 3]\e[0m\n" } { } - group "fn1" - "Creates a function that accepts one anonymous argument and executes the given block with that argument." + group "recur-if" + "Internal recursion control function. Resets function execution if condition > 0, otherwise returns result." { - argsn 1 - pure + argsn 2 argtypes { - 1 [ Block ] + 1 [ Integer ] } - arg `body: Block containing the function body code` - returns `function object that accepts one anonymous argument` + arg `condition: Integer value, if > 0 triggers recursion reset` + returns `nil if recursion continues, ps.Res if recursion ends` } { - equal { fn1 { .pass { } } |type? } 'function - equal { x: fn1 { } , x 123 } 123 - equal { x: fn1 { .pass { } } , x 123 } 123 - equal { x: fn1 { + 1 } , x 123 } 124 } { } - group "fn" - "Creates a function with named parameters specified in the first block and code in the second block." + group "recur-if\\1" + "Internal recursion control function for single-argument functions. Updates first argument and resets if condition > 0." { argsn 2 - pure argtypes { - 1 [ Block ] - 2 [ Block ] + 1 [ Integer ] + 2 [ Integer ] } - arg `spec: Block containing parameter specifications` - arg `body: Block containing the function body code` - returns `function object with the specified parameters` + arg `condition: Integer value, if > 0 triggers recursion with updated argument` + arg `arg1: New value for the first function argument during recursion` + returns `nil if recursion continues, ps.Res if recursion ends` } { - equal { fn { } { } |type? } 'function - equal { x: fn { } { 234 } , x } 234 - equal { x: fn { x } { x } , x 123 } 123 - equal { x: fn { x } { + 123 } , x 123 } 246 - equal { add: fn { a b } { a + b } , add 10 20 } 30 } { } - group "fn\\spec?" - "Returns the argument specification block of a function." + group "recur-if\\2" + "Internal recursion control function for two-argument functions. Updates both arguments and resets if condition > 0." { - argsn 1 - pure + argsn 3 argtypes { - 1 [ Function ] + 1 [ Integer ] + 2 [ Integer ] + 3 [ Integer ] } - arg `function: Function to get the argument spec from` - returns `Block containing the function's parameter specification` + arg `condition: Integer value, if > 0 triggers recursion with updated arguments` + arg `arg1: New value for the first function argument during recursion` + arg `arg2: New value for the second function argument during recursion` + returns `ps.Res (function result) regardless of condition` } { - equal { f: fn { x y } { x + y } , ?f .fn\spec? } { x y } - equal { does { 1 } |fn\spec? } { } } { } - group "fn\\body?" - "Returns the body block of a function." + group "recur-if\\3" + "Internal recursion control function for three-argument functions. Updates all three arguments and resets if condition > 0." { - argsn 1 - pure + argsn 4 argtypes { - 1 [ Function ] + 1 [ Integer ] + 2 [ Integer ] + 3 [ Integer ] + 4 [ Integer ] } - arg `function: Function to get the body from` - returns `Block containing the function's body code` + arg `condition: Integer value, if > 0 triggers recursion with updated arguments` + arg `arg1: New value for the first function argument during recursion` + arg `arg2: New value for the second function argument during recursion` + arg `arg3: New value for the third function argument during recursion` + returns `ps.Res (function result) regardless of condition, nil if recursion continues` } { - equal { f: fn { x y } { x + y } , ?f .fn\body? } { x + y } - equal { does { 123 } |fn\body? } { 123 } } { } - group "pfn" - "Creates a pure function (no side effects allowed) with named parameters and code body." +} + +section "Combinators " "Functions that combine other functions or control execution flow" { + group "pass" + "Accepts a value and a block. It does the block, with value injected, and returns (passes on) the initial value." { argsn 2 - pure argtypes { - 1 [ Block ] 2 [ Block ] } - arg `spec: Block containing parameter specifications` - arg `body: Block containing the function body code` - returns `pure function object with the specified parameters` + arg `value: Any value that will be passed to the block and returned` + arg `block: Block of code to execute with the value injected` + returns `The original value, regardless of what the block returns` } { - equal { pfn { } { } |type? } 'function - equal { x: pfn { x } { + 123 } , x 123 } 246 - ; TODO -- it seems pure namespace not also has print and append! error { x: pfn { } { ?append! } , x 123 } - ; TODO -- it seems pure namespace not also has print and append! error { x: pfn { x } { .print } , x 123 } + equal { 101 .pass { 202 } } 101 + equal { 101 .pass { 202 + 303 } } 101 + equal { "hello" .pass { .length? } } "hello" } { } - group "fn\\cc" - "Creates a function that captures the current context, allowing access to variables from the enclosing scope." + group "wrap" + "Executes a wrapper block before and after executing a main block, returning the result of the main block." { argsn 2 - pure argtypes { 1 [ Block ] 2 [ Block ] } - arg `spec: Block containing parameter specifications` - arg `body: Block containing the function body code` - returns `function object with the current context captured` + arg `wrapper: Block of code to execute before and after the main block` + arg `block: Main block of code to execute between wrapper executions` + returns `The result of the main block execution` } { - equal { fn\cc { x } { x + y } |type? } 'function - equal { y: 5 , f: fn\cc { x } { x + y } , f 3 } 8 + stdout { wrap { prn "*" } { prn "x" } } "*x*" } { } - group "fn\\in" - "Creates a function with a specified parent context, allowing access to variables from that context." + group "keep" + "Do the first block, then the second one but return the result of the first one." { argsn 3 - pure argtypes { - 1 [ Block ] - 2 [ Context ] + 2 [ Block ] 3 [ Block ] } - arg `spec: Block containing parameter specifications` - arg `context: Context object to use as parent context` - arg `body: Block containing the function body code` - returns `function object with the specified parent context` + arg `value: Value to be injected into both blocks` + arg `block1: First block whose result will be returned` + arg `block2: Second block to execute after the first one` + returns `The result of the first block, ignoring the result of the second block` } { - equal { ctx: context { y: 5 } , f: fn\in { x } ctx { x + y } , f 3 } 8 + equal { 20 .keep { + 202 } { + 101 } } 222 } { } - group "fn\\inside" - "Creates a function that executes directly in the specified context rather than creating a new execution context." +} + +section "Default" "" { + group "prns" + "Prints a value followed by a space, returning the input value." { - argsn 3 - pure - argtypes { - 1 [ Block ] - 2 [ Context ] - 3 [ Block ] - } - arg `spec: Block containing parameter specifications` - arg `context: Context object to execute the function in` - arg `body: Block containing the function body code` - returns `function object that executes directly in the specified context` + argsn 1 + arg `value: Value to print` + returns `returns the input value` } { - equal { ctx: context { y: 5 } , f: fn\inside { x } ctx { x + y } , f 3 } 8 + stdout { prns "A" } "A " } { } - group "modder" - "Creates a function that executes directly in the current context (like fn\\inside with current), allowing arguments and context words to be modified with word::." + group "prn" + "Prints a value without adding a newline, returning the input value." { - argsn 2 - argtypes { - 1 [ Block ] - 2 [ Block ] - } - arg `spec: Block containing parameter specifications` - arg `body: Block containing the function body code` - returns `function object that executes directly in the current context (captured at creation), with arguments as variables` + argsn 1 + arg `value: Value to print` + returns `returns the input value` } { - equal { var 'x 0 , inc: modder { } { x:: x + 1 } , inc , inc , x } 2 - equal { var 'n 10 , add: modder { v } { n:: n + v } , add 5 , n } 15 + stdout { prn "A" } "A" } { } - group "closure" - "Creates a closure that captures the current context at creation time, preserving access to variables in that scope." + group "print" + "Prints a value followed by a newline, returning the input value." + { + argsn 1 + arg `value: Value to print` + returns `returns the input value` + } + + { + stdout { print "A" } "A\n" + } + + { + } + + group "print2" + "Prints two values separated by a space and followed by a newline, returning the second value." { argsn 2 - pure - argtypes { - 1 [ Block ] - 2 [ Block ] - } - arg `spec: Block containing parameter specifications` - arg `body: Block containing the function body code` - returns `function object that captures the current context at creation time` + arg `a: First value to print` + arg `b: Second value to print` + returns `returns the second input value` } { - equal { y: 5 , f: closure { x } { x + y } , f 3 } 8 - equal { mk-cntr: does { var 'c 0 , closure { } { inc! 'c } } cnt: mk-cntr , cnt + cnt + cnt } 6 + stdout { print2 "A" "B" } "A B\n" } { } - group "partial" - "Creates a partially applied function with specified arguments, using _ (void) for arguments to be filled later." + group "prn2" + "Prints two values separated by a space without a newline, returning the second value." { argsn 2 - pure - argtypes { - 1 [ Builtin Function ] - 2 [ Block ] - } - arg `func: Function or builtin to partially apply` - arg `args: Block of arguments, with _ (void) for arguments to be filled later` - returns `CurriedCaller object that can be called with the remaining arguments` + arg `a: First value to print` + arg `b: Second value to print` + returns `returns the second input value` } { - equal { prepend-star: partial ?concat [ "* " _ ] , prepend-star "hello" } "* hello" - equal { add-5: partial ?_+ [ _ 5 ] , add-5 10 } 15 - equal { fn-add: fn { x y } { x + y } , add-5: partial ?fn-add [ _ 5 ] , add-5 10 } 15 + stdout { prn2 "A" "B" } "A B" } { } - group "apply" - "Applies a function or builtin to a block of arguments." + group "prns2" + "Prints two values each followed by a space, returning the second value." { argsn 2 - argtypes { - 1 [ Function Builtin VarBuiltin ] - 2 [ Block ] - } - arg `function: Function or builtin to apply` - arg `args: Block of arguments to pass to the function` - returns `The result of applying the function to the arguments` + arg `a: First value to print` + arg `b: Second value to print` + returns `returns the second input value` } { - equal { apply ?_+ { 12 23 } } 35 - equal { apply fn { x y } { x + y } { 5 10 } } 15 - equal { f: fn { x y } { x * y } , apply ?f { 7 6 } } 42 + stdout { prns2 "A" "B" } "A B " } { } - group "on-change" - "Registers an observer block to be executed when a variable changes. Use with 'var' result." + group "format" + "Formats a block of values using a format string with {} placeholders, returning the resulting string." { argsn 2 argtypes { - 1 [ Word ] + 1 [ String ] 2 [ Block ] } - arg `word: Word representing the variable to observe` - arg `observer: Block containing code to execute when variable changes` - returns `The observer block` + arg `template: String with {} placeholders` + arg `values: Block of values to substitute into the template in order` + returns `string with placeholders replaced` } { - equal { x: var 'counter 0 , on-change x { print "changed" } , counter:: 5 } 5 + ; equal { format "x {} y {}" { 1 2 } } "x 1 y 2" + ; error { format 1 { 1 2 } } + ; error { format "{}" 42 } } { } -} - -section "Other " "..." { - group "change!" - "Searches for a word and changes it's value in-place. Only works on variables declared with var. If value changes returns true otherwise false" + group "prnf" + "Formats and prints a value by replacing {} in the template string, without a newline." { argsn 2 argtypes { - 1 [ Word ] + 2 [ String ] } - arg `value: New value to assign to the word` - arg `word: Word whose value should be changed` - returns `Boolean true if the value changed, false if the new value is the same as the old value` + arg `value: Value to embed` + arg `template: String with {} placeholder` + returns `returns the input value after printing` } { - equal { var 'x 123 , change! 234 'x , x } 234 - equal { a:: 123 change! 333 'a a } 333 - equal { a:: 123 change! 124 'a } true - equal { a:: 123 change! 123 'a } false + ; stdout { prnf 42 "Value={}" } "Value=42" } { } - group "modify!" - "Searches for a word and modifies it's value in-place. Only works on variables declared with var. If value changes returns true otherwise false" + group "printf" + "Formats and prints a value by replacing {} in the template string, followed by a newline." { argsn 2 argtypes { - 1 [ Word ] + 2 [ String ] } - arg `value: New value to assign to the word` - arg `word: Word whose value should be changed` - returns `Boolean true if the value changed, false if the new value is the same as the old value, will replace change!` + arg `value: Value to embed` + arg `template: String with {} placeholder` + returns `returns the input value after printing` } { - equal { var 'x 123 , modify! 234 'x , x } 234 - equal { a:: 123 modify! 333 'a a } 333 - equal { a:: 123 modify! 124 'a } true - equal { a:: 123 modify! 123 'a } false + ; stdout { printf 42 "Value={}" } "Value=42\n" } { } - group "set" - "Set word to value or words by deconstructing a block. Only works on variables declared with var." + group "embed" + "Embeds a value into a string or URI by replacing {} placeholder with the string representation of the value." { argsn 2 argtypes { - 1 [ Block ] - 2 [ Block ] + 1 [ String Uri ] } - arg `values: Value or block of values to assign to the word(s)` - arg `words: Word or block of words to be set` - returns `The value or block of values that was assigned` + arg `value: Value to embed` + arg `target: String or URI with {} placeholder` + returns `string or URI with placeholder replaced by the value` } { - equal { set { 123 234 } { a b } b } 234 + equal { embed 42 "v={}" } "v=42" + error { 42 embed 123 } } { } - group "unset!" - "Unset a word in current context, only meant to be used in console" + group "prnv" + "Embeds a value into a string by replacing {} placeholder and prints it without a newline, returning the input value." { - argsn 1 + argsn 2 argtypes { - 1 [ Word ] + 1 [ String ] } - arg `word: Word to be unset from the current context` - returns `Void value` + arg `value: Value to embed` + arg `template: String with {} placeholder` + returns `returns the input value after printing` } { - equal { x: 1 unset! 'x x: 2 } 2 ; otherwise would produce an error + stdout { prnv 42 "v={}" } "v=42" } { } - group "val" - "Returns value of the word in context" + group "printv" + "Embeds a value into a string by replacing {} placeholder and prints it followed by a newline, returning the input value." { - argsn 1 - pure + argsn 2 argtypes { - 1 [ Word ] + 1 [ String ] } - arg `word: Word whose value should be retrieved` - returns `The value associated with the word in the current context` + arg `value: Value to embed` + arg `template: String with {} placeholder` + returns `returns the input value after printing` } { - equal { x: 123 val 'x } 123 - equal { x: 123 y: 'x val y } 123 + stdout { printv 42 "v={}" } "v=42\n" } { } - group "_<<" - "Converts a value to specific kind (R to L)" + group "probe\\" + "Prints a prefix string followed by detailed type and value information about a value, followed by a newline, returning the input value." { argsn 2 argtypes { - 1 [ Kind ] - 2 [ Dict Context ] + 2 [ String ] } - arg `kind: Kind to convert the value to` - arg `value: Dict or context to convert` - returns `A new context of the specified kind` } { - ; TODO equal { person: kind 'person { name: "" age: 0 } person << dict { "name" "John" "age" 30 } |type? } 'ctx } { } - group "_|" - "Pipeline operator that passes the value through unchanged (used with 'not' and other operations)." + group "inspect" + "Returns a string containing detailed type and value information about a value." { argsn 1 - pure - arg `value: Any value to be passed through unchanged` - returns `the original value (used in pipeline operations for explicit pass-through)` + arg `value: Value to inspect` + returns `string with diagnostic representation` } { - equal { 5 | } 5 - equal { "hello" | } "hello" - equal { true | } true - equal { { 1 2 3 } | } { 1 2 3 } + ; equal { 42 inspect |type? } 'string } { } - group "doc!" - "Sets docstring of the current context." + group "esc" + "Creates an ANSI escape sequence by prepending the escape character (\\033) to the input string." { argsn 1 - pure argtypes { 1 [ String ] } - arg `doc: String to set as the docstring for the current context` - returns `Integer 1 on success` + arg `str: Base string to prefix with escape character` + returns `string starting with escape character` } { - equal { x: private { doc! "some doc" doc? } } "some doc" + equal { "A" esc } "\033A" } { } - group "doc?" - "Gets docstring of the current context." + group "esc-val" + "Creates an ANSI escape sequence with an embedded value by replacing {} placeholder and prepending the escape character (\\033)." { - pure - arg `None` - returns `String containing the docstring of the current context` + argsn 2 + argtypes { + 2 [ String ] + } + arg `value: Value to embed` + arg `template: String with {} placeholder` + returns `string starting with escape character and embedded value` } { - equal { x: private { doc! "some doc" doc? } } "some doc" + equal { esc-val 42 "X={}" } "\033X=42" } { } - group "doc\\of?" - "Get docstring of the passed context." +} + +section "Failure " "Handling failures" { + group "fail" + "Creates an error and sets the failure flag, but continues execution (unlike ^fail)." { argsn 1 - pure - argtypes { - 1 [ Context PersistentContext ] - } - arg `value: Function, builtin, or context to get the docstring from` - returns `String containing the docstring of the provided value` + arg `error_info: String message, Integer code, or block for multiple parameters` + returns `error object and sets the failure flag` } { - equal { x: context { doc! "some doc" } doc\of? x } "some doc" + equal { try { fail "error message" } |type? } 'error + equal { try { fail "error message" } |message? } "error message" + equal { try { fail 404 } |status? } 404 } { } - group "ref" - "Makes a value mutable instead of immutable" + group "^fail" + "Creates an error and immediately returns from the current function with failure state." { argsn 1 - pure - argtypes { - 1 [ Table Dict List Block String Native Context ] - } - arg `value: Value to make mutable` - returns `A mutable reference to the value` + arg `error_info: String message, Integer code, or block for multiple parameters` + returns `error object and sets both failure and return flags` } { - equal { is-ref ref { 1 2 3 } } true - equal { ref { 1 2 3 } |type? } 'block - equal { ref dict { "a" 1 } |type? } 'dict - equal { ref list { 1 2 3 } |type? } 'list - equal { ref "hello" |disarm |type? } 'string - ; TODO equal { b: ref { 1 2 3 } , append! b 4 , deref b |length? } 4 ; mutable in-place append + equal { ff:: fn { } { ^fail "error message" } ff |disarm |type? } 'error + equal { ff:: fn { } { ^fail "error message" } ff |disarm |message? } "error message" + equal { ff:: fn { } { ^fail 404 } ff |disarm |status? } 404 + equal { ff:: fn { } { ^fail 'user-error } ff |disarm |kind? } 'user-error } { } - group "deref" - "Makes a value again immutable" + group "refail" + "Re-raises an existing error with additional context. TODO -- duplicate of check" { - argsn 1 - pure + argsn 2 argtypes { - 1 [ Table Dict List Block String Native Context ] + 1 [ Error ] } - arg `value: Mutable reference to make immutable` - returns `An immutable copy of the value` } { - equal { is-ref deref ref { 1 2 3 } } false - equal { deref ref { 1 2 3 } } { 1 2 3 } - equal { deref ref "hello" } "hello" - equal { deref ref list { 1 2 3 } |length? } 3 - equal { ref dict { "a" 1 } |deref |type? } 'dict } { } - group "is-ref" - "Checks if a value is a mutable reference." + group "failure" + "Creates an error object without setting any flags (unlike fail and ^fail)." { argsn 1 - arg `value: Any value to check if it's a reference` - returns `Integer 1 if the value is a reference, 0 otherwise` - } - - { - equal { ref { } |is-ref } true - equal { { } |is-ref } false - equal { ref "hello" |is-ref } true - equal { "hello" |is-ref } false - equal { ref list { 1 2 3 } |is-ref } true - equal { 123 |is-ref } false - } - - { - } - - group "is-same" - "Checks if two values are the same object (identity comparison, not value comparison)." - { - argsn 2 - pure - arg `value1: First value to compare` - arg `value2: Second value to compare` - returns `Boolean true if values are the same object (identity), false otherwise` + arg `error_info: String message, Integer code, or block for multiple parameters` + returns `error object without setting any flags` } { - equal { a: ref [ 1 2 ] is-same a a } true - equal { a: ref [ 1 2 ] b: ref [ 1 2 ] is-same a b } false - equal { a: ref [ 1 2 ] b: a is-same a b } true - equal { is-same 123 123 } true - equal { is-same "abc" "abc" } false ; strings are different objects + equal { failure "error message" |type? } 'error + equal { failure "error message" |message? } "error message" + equal { failure 404 |status? } 404 } { } - group "dict" - "Constructs a Dict from the Block of key and value pairs." + group "empty" + "Creates an 404 error object without setting any flags." { - argsn 1 - pure - arg `block: Block containing alternating keys and values` - returns `A new Dict with the specified keys and values` + arg `error_info: String message, Integer code, or block for multiple parameters` + returns `error object without setting any flags` } { - equal { dict { "a" 123 } |-> "a" } 123 - equal { dict { "name" "John" "age" 30 } |-> "name" } "John" - equal { dict { } |type? } 'dict - equal { dict { "x" 1 "y" 2 "z" 3 } |length? } 3 - error { dict { 123 "value" } } ; integer keys should fail - error { dict { { } "value" } } ; block keys should fail - equal { dict { 'key "value" } |-> "key" } "value" ; tagwords as keys + equal { failure "error message" |type? } 'error + equal { failure "error message" |message? } "error message" + equal { failure 404 |status? } 404 } { } - group "change" - "Returns a new Dict with the specified key changed to the new value. Original dict is unchanged." + group "failure\\wrap" + "Creates a new error that wraps an existing error, allowing for error chaining." { - argsn 3 - pure + argsn 2 argtypes { - 1 [ Dict ] - 2 [ String Word ] + 2 [ Error ] } - arg `dict: Dict to update` - arg `key: String key to change` - arg `value: New value for the key` - returns `A new Dict with the key updated (original dict unchanged)` + arg `error_info: String message, Integer code, or block for multiple parameters` + arg `error: Error object to wrap` + returns `new error object that wraps the provided error` } { - equal { dict { "a" 1 "b" 2 } |change "a" 99 |-> "a" } 99 - equal { dict { "a" 1 "b" 2 } |change "c" 3 |-> "c" } 3 - equal { d: dict { "x" 10 } , change d "x" 20 |-> "x" } 20 + equal { failure\wrap "outer error" failure "inner error" |message? } "outer error" + equal { failure\wrap "outer error" failure "inner error" |type? } 'error } { } - group "list" - "Constructs a List from the Block of values." + group "is-error" + "Returns true if the value is an error, false otherwise." { argsn 1 - pure - arg `block: Block containing values to put in the list` - returns `A new List with the values from the block` + arg `value: Any value to check` + returns `boolean true if the value is an error, false otherwise` } { - equal { list { "a" 123 } |-> 0 } "a" - equal { list { 1 2 3 } |length? } 3 - equal { list { } |length? } 0 - equal { list { "hello" "world" } |-> 1 } "world" - equal { list { 1 2 3 } |type? } 'list - equal { list { 1 2 3 } |first } 1 - equal { list { 1 2 3 } |last } 3 + equal { is-error failure "test" } true + equal { is-error 123 } false } { } - group "load" - "Loads a string into Rye values." + group "error-kind?" + "Returns the kind of an error, or void if not an error." { argsn 1 - argtypes { - 1 [ String ] - } - arg `source: String containing Rye code to load` - returns `Block containing the parsed Rye values` + arg `value: Any value to check` + returns `the kind of the error as a word, or void if not an error` } { - equal { load " 1 2 3 " |third } 3 - equal { load "{ 1 2 3 }" |first |third } 3 + equal { error-kind? failure { 'syntax-error 404 "Syntax error" } } 'syntax-error + equal { error-kind? 123 } _ } { } - group "load\\mod" - "Loads a string into Rye values. During load it allows modification of words." + group "is-error-of-kind" + "Returns true if the value is an error of the specified kind, false otherwise." { - argsn 1 + argsn 2 argtypes { - 1 [ String ] + 2 [ Word Tagword ] } - arg `source: String containing Rye code or URI of file to load with modification allowed` - returns `Block containing the parsed Rye valuesload\mod - string-only; file URI variant moved to builtins_baseio.go as "load\\mod\\file"` + arg `error: Error object to check` + arg `kind: Word or tagword representing the error kind to check against` + returns `boolean true if the error is of the specified kind, false otherwise` } { - ; load\mod file://modifiable.rye ; loads file with word modification allowed + equal { is-error-of-kind failure { 'syntax-error 404 "Syntax error" } 'syntax-error } true + equal { is-error-of-kind failure { 'syntax-error 404 "Syntax error" } 'runtime-error } false } { } - group "load\\live" - "Loads a string into Rye values. During load it allows modification of words." + group "cause?" + "Extracts the root cause from an error chain by traversing the Parent references." { argsn 1 argtypes { - 1 [ String ] + 1 [ Error ] } - arg `source: String containing Rye code to load with modification allowed and file watching` - returns `Block containing the parsed Rye valuesload\live - string-only; file URI variant moved to builtins_baseio.go` + arg `error: Error object to extract the root cause from` + returns `the root cause error from an error chain` } { - ; load\live file://watched.rye ; loads and watches file for changes + equal { cause? failure\wrap "outer error" failure "inner error" |message? } "inner error" + equal { cause? failure "single error" |message? } "single error" } { } - group "load\\sig" - "Checks the signature, if OK then loads a string into Rye values." + group "status?" + "Extracts the numeric status code from an error object." { argsn 1 - arg `source: String containing signed Rye code to verify and load` - returns `Block containing the parsed Rye values if signature is valid` + argtypes { + 1 [ Error ] + } + arg `error: Error object to extract status code from` + returns `integer status code of the error` } { - ; load\sig "signed-code" ; loads only if signature is valid + equal { failure 404 |status? } 404 + equal { failure "message" |status? } 0 + error { "not an error" |status? } } { } - group "do" - "Takes a block of code and does (runs) it." + group "message?" + "Extracts the message string from an error object." { argsn 1 - pure argtypes { - 1 [ Block ] + 1 [ Error ] } - arg `block: Block of code to execute` - returns `result of executing the block` + arg `error: Error object to extract message from` + returns `string message of the error` } { - equal { do { 123 + 123 } } 246 - error { do { 123 + } } - equal { do { _+ _+ 12 23 34 } } 69 - equal { do { 12 * 23 |+ 34 } } 310 - equal { do { ( 12 * 23 ) + 34 } } 310 - equal { do { 12 * 23 | + 34 } } 310 - equal { do { 12 * 23 :a + 34 } } 310 - equal { do { 12 * 23 :a a + 34 } } 310 + equal { failure "error message" |message? } "error message" + equal { failure 404 |message? } "" + error { "not an error" |message? } } { } - group "with" - "Takes a value and a block of code. It does the code with the value injected." + group "details?" + "Extracts additional details from an error object as a dictionary." { - argsn 2 + argsn 1 argtypes { - 2 [ Block ] + 1 [ Error ] } - arg `value: Value to inject into the block's execution context` - arg `block: Block of code to execute with the injected value` - returns `result of executing the block with the injected value` + arg `error: Error object to extract additional details from` + returns `dictionary containing any additional values stored in the error` } { - equal { with 100 { + 11 } } 111 - equal { with 100 { + 11 , * 3 } } 300 + ; equal { failure { "code" 404 "info" "Not Found" } |details? |type? } 'dict + ; equal { failure { "code" 404 "info" "Not Found" } |details? .code } 404 + error { "not an error" |details? } } { } - group "do\\inside" - "Takes a Context and a Block. It Does a block inside a given Context." + group "has-failed" + "Tests if a value is an error object, returning true for errors and false for non-errors." { - argsn 2 - argtypes { - 1 [ Context PersistentContext ] - 2 [ Block ] - } - arg `context: Context in which to execute the block` - arg `block: Block of code to execute within the specified context` - returns `result of executing the block within the given context` + argsn 1 + arg `value: Any value to check` + returns `boolean true if the value is an error, false otherwise` } { - equal { c: context { x: 100 } do\inside c { x * 9.99 } } 999.0 - equal { c: context { x:: 100 } do\inside c { inc! 'x } } 101 - equal { c: context { var 'x 100 } do\inside c { x:: 200 } c/x } 200 - equal { c: context { x:: 100 } do\inside c { x:: 200 , x } } 200 + equal { has-failed failure "error" } true + equal { has-failed "not an error" } false + equal { has-failed 123 } false + equal { has-failed try { fail "error" } } true } { } - group "do\\in" - "Takes a Context and a Block. It Does a block in current context but with parent a given Context." + group "is-failure" + "Checks if a value is a failure/error type. Unlike has-failed, this doesn't accept failures - the value must already be disarmed." { - argsn 2 - argtypes { - 1 [ Context ] - 2 [ Block ] - } - arg `context: Context to use as parent context during execution` - arg `block: Block of code to execute in current context with the specified parent context` - returns `result of executing the block with the modified parent context` + argsn 1 + arg `value: Any value to check (must already be disarmed if it was a failure)` + returns `boolean true if the value is an error type, false otherwise` } { - equal { c: context { x: 100 } do\in c { x * 9.99 } } 999.0 - equal { c: context { x:: 100 } do\in c { inc! 'x } } 101 - equal { c: context { x: 100 } do\in c { x:: 200 , x } } 200 - equal { c: context { x: 100 } do\in c { x:: 200 } c/x } 100 + equal { try { fail "error" } |disarm |is-failure } true + equal { failure "error" |is-failure } true + equal { 123 |is-failure } false + equal { "hello" |is-failure } false } { } - group "do\\inx" - "Takes a Context and a Block. It Does a block in current context but with parent a given Context." + group "is-success" + "Returns true for any value that is not a failure/error type. The opposite of is-failure." { - argsn 2 - argtypes { - 1 [ Context ] - 2 [ Block ] - } - arg `context: Context to use as parent context during execution` - arg `block: Block of code to execute in current context with the specified parent context` - returns `result of executing the block with the modified parent context` + argsn 1 + arg `value: Any value to check (must already be disarmed if it was a failure)` + returns `boolean true if the value is not an error type, false if it is an error` } { - equal { c: context { x: 100 } do\in c { x * 9.99 } } 999.0 - equal { c: context { x:: 100 } do\in c { inc! 'x } } 101 - equal { c: context { x: 100 } do\in c { x:: 200 , x } } 200 - equal { c: context { x: 100 } do\in c { x:: 200 } c/x } 100 + equal { 123 |is-success } true + equal { "hello" |is-success } true + equal { { 1 2 3 } |is-success } true + equal { failure "error" |is-success } false + equal { try { fail "error" } |disarm |is-success } false } { } - group "lk\\" - "Lists available kinds with string filter" + group "disarm" + "Clears the failure flag while preserving the error object, allowing error inspection without propagation." { argsn 1 - argtypes { - 1 [ String ] - } + arg `error: Error object to disarm` + returns `the original error object, but clears the failure flag` } { + equal { try { fail "error" |disarm } |type? } 'error + equal { try { fail "error" |disarm |message? } } "error" + equal { try { fail "error" } |has-failed } true + equal { try { fail "error" |disarm } |has-failed } true } { } - group "lg\\" - "Lists generic words related to specific kind with string filter" + group "check" + "Checks if a value is in failure state and wraps it with a new error if so, otherwise returns the original value." { argsn 2 - argtypes { - 2 [ String ] - } + arg `value: Value to check for failure state` + arg `error_info: Error information to use if value is in failure state` + returns `original value if not in failure state, or a new error wrapping the original error` } { + equal { 5 |check "Value must be positive" } 5 + equal { try { fail "Original error" |check "Wrapped error" } |message? } "Wrapped error" + equal { try { fail "Original error" |check "Wrapped error" } |details? |type? } 'dict } { } - group "return" - "Accepts one value and returns it." + group "^check" + "Like 'check' but also sets the return flag to immediately exit the current function." { - argsn 1 + argsn 2 + arg `value: Value to check for failure state` + arg `error_info: Error information to use if value is in failure state` + returns `original value if not in failure state, or immediately returns from function with a new error` } { - equal { x: fn { } { return 101 202 } x } 101 + equal { fn { x } { x |^check "Error in function" } |apply [ 5 ] } 5 + equal { ff:: fn { x } { fail "Original" |^check "Wrapped" } ff 5 |disarm |message? } "Wrapped" } { } - group "rye" - "" + group "^ensure" + "Checks if a value is truthy and returns it if so, otherwise creates an error and immediately returns from the function." { + argsn 2 + arg `condition: Value to test for truthiness` + arg `error_info: Error information to use if condition is not truthy` + returns `condition value if truthy, or immediately returns from function with an error` } { - equal { rye .type? } 'native + equal { fn { x } { x |^ensure "Must be positive" } |apply [ 5 ] } true + equal { ff:: fn { x } { x > 0 |^ensure "Must be positive" } ff -1 |disarm |message? } "Must be positive" } { } - group "Rye-itself//Capture" - "Registers a capture block that will be executed with the error injected if an unhandled error bubbles to the top of the program. The error value is injected as the first value (use .disarm, .print, etc.)." + group "ensure" + "Checks if a value is truthy and returns it if so, otherwise creates an error with the failure flag set." { argsn 2 - argtypes { - 2 [ Block ] - } - arg `rye-itself: The rye native object (passed via dot-word dispatch)` - arg `block: Block of code to execute with the captured error injected when an error bubbles to top-level` - returns `The rye-itself native (so the chain continues cleanly)` + arg `condition: Value to test for truthiness` + arg `error_info: Error information to use if condition is not truthy` + returns `condition value if truthy, or creates an error with failure flag set` } { - stdout { rye .Capture { .disarm .print } 1 / 0 } "Error: { message: division by zero }\n" - stdout { rye .Capture { .disarm .print } "ok" } "" + equal { 5 > 0 |ensure "Must be positive" } true + equal { try { -1 > 0 |ensure "Must be positive" } |message? } "Must be positive" } { } - group "defer" - "Registers a block of code to be executed when the current function exits or the program terminates." + group "requires-one-of" + "Validates that a value matches one of the provided options, failing with a descriptive error if not." { - argsn 1 + argsn 2 argtypes { - 1 [ Block ] + 2 [ Block ] } + arg `value: Value to check against valid options` + arg `options: Block containing valid values` + returns `original value if it matches one of the options, or creates an error with failure flag set` } { - equal { x:: 123 defer { x:: 345 } x } 123 - stdout { ff:: fn { } { var 'x 123 defer { print 234 } x } , ff } "234\n" - equal { ff:: fn { } { x:: 123 defer { x:: 234 } x + 111 } , ff } 234 ; the result of defer expression is returned TODO, change this + equal { "a" |requires-one-of { "a" "b" "c" } } "a" + equal { "b" |requires-one-of { "a" "b" "c" } } "b" + equal { try { "x" |requires-one-of { "a" "b" "c" } } |message? |contains "must be one of" } true + equal { try { "x" |requires-one-of { "a" "b" "c" } } |message? |contains "[String: x]" } true + equal { try { 5 |requires-one-of { 1 2 3 } } |message? |contains "5" } true } { } - group "defer\\" - "Registers a block of code with an injected value to be executed when the current function exits or the program terminates. Works like 'with' but deferred." + group "^requires-one-of" + "Validates that a value matches one of the provided options, failing with a descriptive error if not." { argsn 2 argtypes { 2 [ Block ] } - arg `value: Value to inject into the deferred block` - arg `block: Block to execute with the injected value when function exits` - returns `Void value` + arg `value: Value to check against valid options` + arg `options: Block containing valid values` + returns `original value if it matches one of the options, or creates an error with failure flag set` } { - equal { x:: 0 defer\ 42 { + 1 } x } 0 - stdout { ff:: fn { } { defer\ "hello" { .print } "done" } , ff } "hello\n" + equal { "a" |requires-one-of { "a" "b" "c" } } "a" + equal { "b" |requires-one-of { "a" "b" "c" } } "b" + equal { try { "x" |requires-one-of { "a" "b" "c" } } |message? |contains "must be one of" } true + equal { try { "x" |requires-one-of { "a" "b" "c" } } |message? |contains "[String: x]" } true + equal { try { 5 |requires-one-of { 1 2 3 } } |message? |contains "5" } true } { } - group "assert" - "Evaluates a block of code and asserts that its result equals the expected value. Returns the result if assertion passes, fails with error message if not." + group "ensure\\with" + "Injects a value into a validation block and returns the value if the block is truthy, otherwise creates an error." { - argsn 2 + argsn 3 argtypes { - 1 [ Block ] + 2 [ Block ] } - arg `block: Block of code to evaluate` - arg `expected: Expected result value` - returns `The result value if assertion passes, error if it fails` + arg `value: Value to inject into the validation block` + arg `block: Block that receives the value and returns truthy/falsy result` + arg `error_info: Error information to use if block returns falsy` + returns `original value if block returns truthy, or creates an error with failure flag set` } { - equal { assert { 1 + 2 } 3 } 3 - equal { assert { "hello" } "hello" } "hello" - error { assert { 1 + 2 } 5 } + equal { dict { "status" 200 } |ensure\with { -> "status" = 200 } "status check failed" } dict { "status" 200 } + equal { try { dict { "status" 404 } |ensure\with { -> "status" = 200 } "status check failed" } |message? } "status check failed" } { } - group "assert\\display" - "Evaluates a block of code and displays the result. Prints the explanation in magenta followed by OK in green on success, or error message in red on failure. Does not fail - continues evaluation." + group "^ensure\\with" + "Like ensure\\with but also sets the return flag to immediately exit the current function." { argsn 3 argtypes { - 1 [ String ] 2 [ Block ] } - arg `explanation: String describing the test being performed` - arg `block: Block of code to evaluate` - arg `expected: Expected result value` - returns `The result value (displays status but continues evaluation even on failure)` + arg `value: Value to inject into the validation block` + arg `block: Block that receives the value and returns truthy/falsy result` + arg `error_info: Error information to use if block returns falsy` + returns `original value if block returns truthy, or immediately returns from function with an error` } { - stdout { assert\display "check addition" { 1 + 2 } 3 } "\e[35mcheck addition\e[0m: \e[32mOK\e[0m\n" - ; stdout { assert\display "check addition" { 1 + 2 } 5 } "\e[35mcheck addition\e[0m: \e[31FAILED: expected `[Integer: 5]` but got `[Integer: 3]\e[0m\n" + ; equal { fn { req } { req |^ensure\with { .Status? = 200 } "request failed" } |apply [ Request "GET" "http://example.com" ] } Request "GET" "http://example.com" + equal { ff:: fn { val } { val |^ensure\with { -> "status" = 200 } "status check failed" } ff dict { "status" 404 } |disarm |message? } "status check failed" } { } - group "recur-if" - "Internal recursion control function. Resets function execution if condition > 0, otherwise returns result." + group "fix" + "Handles errors by executing a block if the value is in failure state, clearing the failure flag." { argsn 2 argtypes { - 1 [ Integer ] + 2 [ Block ] } - arg `condition: Integer value, if > 0 triggers recursion reset` - returns `nil if recursion continues, ps.Res if recursion ends` + arg `value: Value to check for failure state` + arg `handler: Block to execute if value is in failure state` + returns `original value if not in failure state, or result of executing the handler block` } { + equal { 5 |fix { + 10 } } 5 + equal { try { fail "error" |fix { "fixed" } } } "fixed" + equal { try { fail "error" |fix { fail "new error" } } |message? } "new error" } { } - group "recur-if\\1" - "Internal recursion control function for single-argument functions. Updates first argument and resets if condition > 0." + group "^fix" + "Like 'fix' but also sets the return flag to immediately exit the current function with the handler result." { argsn 2 argtypes { - 1 [ Integer ] - 2 [ Integer ] + 2 [ Block ] } - arg `condition: Integer value, if > 0 triggers recursion with updated argument` - arg `arg1: New value for the first function argument during recursion` - returns `nil if recursion continues, ps.Res if recursion ends` + arg `value: Value to check for failure state` + arg `handler: Block to execute if value is in failure state` + returns `original value if not in failure state, or immediately returns from function with handler result` } { + equal { fn { x } { x |^fix { "fixed" } } |apply [ 5 ] } 5 + equal { ff:: fn { x } { fail "error" |^fix { "fixed" } } ff 5 } "fixed" } { } - group "recur-if\\2" - "Internal recursion control function for two-argument functions. Updates both arguments and resets if condition > 0." + group "fix\\either" + "Executes one of two blocks depending on whether the value is in failure state." { argsn 3 argtypes { - 1 [ Integer ] - 2 [ Integer ] - 3 [ Integer ] + 2 [ Block ] + 3 [ Block ] } - arg `condition: Integer value, if > 0 triggers recursion with updated arguments` - arg `arg1: New value for the first function argument during recursion` - arg `arg2: New value for the second function argument during recursion` - returns `ps.Res (function result) regardless of condition` + arg `value: Value to check for failure state` + arg `error_handler: Block to execute if value is in failure state` + arg `success_handler: Block to execute if value is not in failure state` + returns `result of executing the appropriate handler block` } { + equal { fix\either failure "error" { "fixed" } { "not fixed" } } "fixed" + equal { fix\either 5 { "fixed" } { "not fixed" } } "not fixed" } { } - group "recur-if\\3" - "Internal recursion control function for three-argument functions. Updates all three arguments and resets if condition > 0." + group "fix\\else" + "Executes a block if the value is not in failure state, otherwise returns the original value." { - argsn 4 + argsn 2 argtypes { - 1 [ Integer ] - 2 [ Integer ] - 3 [ Integer ] - 4 [ Integer ] + 2 [ Block ] } - arg `condition: Integer value, if > 0 triggers recursion with updated arguments` - arg `arg1: New value for the first function argument during recursion` - arg `arg2: New value for the second function argument during recursion` - arg `arg3: New value for the third function argument during recursion` - returns `ps.Res (function result) regardless of condition, nil if recursion continues` + arg `value: Value to check for failure state` + arg `success_handler: Block to execute if value is not in failure state` + returns `result of executing the success handler if value is not in failure state, or the original value` } { + equal { 5 |fix\else { "not fixed" } } "not fixed" + equal { try { fail "error" |fix\else { "not fixed" } } |message? } "error" } { } -} - -section "Combinators " "Functions that combine other functions or control execution flow" { - group "pass" - "Accepts a value and a block. It does the block, with value injected, and returns (passes on) the initial value." + group "fix\\continue" + "Executes one of two blocks depending on whether the value is in failure state, like an error-handling if/else." { - argsn 2 + argsn 3 argtypes { 2 [ Block ] + 3 [ Block ] } - arg `value: Any value that will be passed to the block and returned` - arg `block: Block of code to execute with the value injected` - returns `The original value, regardless of what the block returns` + arg `value: Value to check for failure state` + arg `error_handler: Block to execute if value is in failure state` + arg `success_handler: Block to execute if value is not in failure state` + returns `result of executing the appropriate handler block` } { - equal { 101 .pass { 202 } } 101 - equal { 101 .pass { 202 + 303 } } 101 - equal { "hello" .pass { .length? } } "hello" + equal { 5 |fix\continue { "error handler" } { "success handler" } } "success handler" + equal { try { fail "error" |fix\continue { "error handler" } { "success handler" } } } "error handler" } { } - group "wrap" - "Executes a wrapper block before and after executing a main block, returning the result of the main block." + group "continue" + "Executes a block only if the value is not in failure state, opposite of 'fix'." { argsn 2 argtypes { - 1 [ Block ] 2 [ Block ] } - arg `wrapper: Block of code to execute before and after the main block` - arg `block: Main block of code to execute between wrapper executions` - returns `The result of the main block execution` + arg `value: Value to check for failure state` + arg `block: Block to execute if value is not in failure state` + returns `result of executing the block if value is not in failure state, or the original value` } { - stdout { wrap { prn "*" } { prn "x" } } "*x*" + equal { 5 |continue { + 10 } } 15 + equal { try { fail "error" |continue { + 10 } } } failure "error" } { } - group "keep" - "Do the first block, then the second one but return the result of the first one." + group "^fix\\match" + "Error handling switch that matches error codes with handler blocks and sets the return flag." { - argsn 3 + argsn 2 argtypes { 2 [ Block ] - 3 [ Block ] } - arg `value: Value to be injected into both blocks` - arg `block1: First block whose result will be returned` - arg `block2: Second block to execute after the first one` - returns `The result of the first block, ignoring the result of the second block` + arg `error: Error object to match against` + arg `cases: Block containing error codes and corresponding handler blocks` + returns `result of executing the matching handler block, or the original error if no match` } { - equal { 20 .keep { + 202 } { + 101 } } 222 + equal { ^fix\match failure 404 { 404 { "Not Found" } 500 { "Server Error" } } } "Not Found" + equal { ^fix\match failure 500 { 404 { "Not Found" } 500 { "Server Error" } } } "Server Error" + equal { ^fix\match failure 403 { 404 { "Not Found" } 500 { "Server Error" } _ { "Unknown Error" } } } "Unknown Error" } { } -} - -section "Default" "" { - group "format" - "Formats a block of values using a format string with {} placeholders, returning the resulting string." + group "try" + "Takes a block of code and does (runs) it." { - argsn 2 + argsn 1 + pure argtypes { - 1 [ String ] - 2 [ Block ] + 1 [ Block ] } } { + equal { try { 123 + 123 } } 246 + equal { try { 123 + "asd" } |type? } 'error + equal { try { 123 + } |type? } 'error + equal { try { fail "oops" } |message? } "oops" } { } - group "prnf" - "Formats and prints a value by replacing {} in the template string, without a newline." + group "try-all" + "Executes a block and returns a result tuple [success, result], where success is true if no error occurred." { - argsn 2 + argsn 1 + pure argtypes { - 2 [ String ] + 1 [ Block ] } + arg `block: Block of code to execute` + returns `a block containing [success, result], where success is true if no error occurred` } { + equal { try-all { 1 + 2 } } [ true 3 ] + equal { try-all { fail "custom error" } |first } false } { } - group "printf" - "Formats and prints a value by replacing {} in the template string, followed by a newline." + group "try\\in" + "Takes a Context and a Block. It Does a block inside a given Context." { argsn 2 argtypes { - 2 [ String ] + 1 [ Context ] + 2 [ Block ] } } { + equal { c: context { x: 100 } try\in c { x * 9.99 } } 999.0 + equal { c: context { x:: 100 } try\in c { inc! 'x } } 101 + equal { c: context { x:: 100 } try\in c { x:: 200 , x } } 200 + equal { c: context { x:: 100 } try\in c { x:: 200 } c/x } 200 + equal { c: context { x: 100 } try\in c { inc! 'y } |type? } 'error } { } - group "embed" - "Embeds a value into a string or URI by replacing {} placeholder with the string representation of the value." + group "finally" + "Executes a block and ensures another block is executed afterward, regardless of errors." { argsn 2 argtypes { - 1 [ String Uri ] + 1 [ Block ] + 2 [ Block ] } + arg `main-block: Block of code to execute` + arg `finally-block: Block to execute afterward, regardless of errors` + returns `result of the main block, preserving any failure state` } { + equal { finally { 1 + 2 } { print "cleanup" } } 3 + equal { try { finally { fail "error" } { print "cleanup" } } |message? } "error" } { } - group "prnv" - "Embeds a value into a string by replacing {} placeholder and prints it without a newline, returning the input value." + group "retry" + "Executes a block and retries it up to N times if it results in a failure." { argsn 2 argtypes { - 1 [ String ] + 1 [ Integer ] + 2 [ Block ] } + arg `retries: Integer number of retries to attempt` + arg `block: Block of code to execute and potentially retry` + returns `result of the block if successful, or the last failure if all retries fail` } { + ; equal { retry 3 { fail 101 } |disarm |type? } 'error + ; equal { retry 3 { fail 101 } |disarm |status? } 101 + equal { retry 3 { 10 + 1 } } 11 } { } - group "printv" - "Embeds a value into a string by replacing {} placeholder and prints it followed by a newline, returning the input value." + group "persist" + "Executes a block repeatedly until it succeeds (no failure), then returns the successful result. Gives up after 1000 attempts." { - argsn 2 + argsn 1 argtypes { - 1 [ String ] + 1 [ Block ] } + arg `block: Block of code to execute repeatedly until it succeeds` + returns `result of the block when it finally succeeds (no failure), or error if 1000 attempts exceeded` } { + equal { persist { 10 + 1 } } 11 + ; equal { counter:: 0 persist { counter:: counter + 1 , either counter > 3 { counter } { fail "not ready" } } } 4 + error { persist { fail "always fails" } } } { } - group "probe\\" - "Prints a prefix string followed by detailed type and value information about a value, followed by a newline, returning the input value." + group "timeout" + "Executes a block of code with a timeout, failing if execution exceeds the specified duration in milliseconds." { argsn 2 argtypes { - 2 [ String ] + 1 [ Integer ] + 2 [ Block ] } + arg `ms: Integer timeout duration in milliseconds` + arg `block: Block of code to execute with a timeout` + returns `result of the block if it completes within the timeout, or a timeout error` } { + equal { timeout 5000 { "ok" } } "ok" + equal { try { timeout 100 { sleep 1000 , "ok" } } |message? |contains "timed out" } true } { } - group "esc" - "Creates an ANSI escape sequence by prepending the escape character (\\033) to the input string." +} + +section "Fast Math " "Fast infix math evaluator" { + group "mth" + "Fast infix math evaluator with proper operator precedence (+,-,*,/,//,%,<,>,=,<=,>=,!=). Single-pass Shunting-yard, direct Go arithmetic, no builtin lookup." { argsn 1 + pure argtypes { - 1 [ String ] + 1 [ Block ] } + arg `block: Block containing an infix math expression` + returns `result of evaluating the expression with proper operator precedence` } { + equal { mth { 1 + 2 } } 3 + equal { mth { 2 + 3 * 4 } } 14 + equal { mth { 10 - 5 - 2 } } 3 + equal { mth { 8 // 3 } } 2 + equal { mth { 8 % 3 } } 2 + equal { mth { 5 + 5 < 10 } } false + equal { mth { 5 + 5 <= 10 } } true + equal { mth { 5 + 4 < 10 } } true + equal { mth { 1 + 2 * 3 } } 7 + equal { a: 5 mth { a * 2 } } 10 + equal { a: 3 b: 4 mth { a * a + b * b } } 25 } { } - group "esc-val" - "Creates an ANSI escape sequence with an embedded value by replacing {} placeholder and prepending the escape character (\\033)." +} + +section "Vector " "Vector operations" { + group "vector" + "Creates a vector from a block of numbers." { - argsn 2 - argtypes { - 2 [ String ] - } + argsn 1 + arg `block: block of numbers to convert to a vector` + returns `vector object` } { + equal { vector [ 1 2 3 ] |type? } 'vector + equal { vector [ 1 2 3 ] |length? } 3 } { } -} - -section "Default" "" { -} - -section "Failure " "Handling failures" { - group "fail" - "Creates an error and sets the failure flag, but continues execution (unlike ^fail)." + group "normalize" + "Calculates the L2 norm (Euclidean length) of a vector." { argsn 1 - arg `error_info: String message, Integer code, or block for multiple parameters` - returns `error object and sets the failure flag` + arg `vector: vector object to normalize` + returns `decimal representing the L2 norm (Euclidean length) of the vector` } { - equal { try { fail "error message" } |type? } 'error - equal { try { fail "error message" } |message? } "error message" - equal { try { fail 404 } |status? } 404 + equal { vector [ 3 4 ] |normalize } 5.0 } { } - group "^fail" - "Creates an error and immediately returns from the current function with failure state." + group "std-deviation" + "Calculates the standard deviation of a vector's elements." { argsn 1 - arg `error_info: String message, Integer code, or block for multiple parameters` - returns `error object and sets both failure and return flags` + arg `vector: vector object` + returns `decimal representing the standard deviation of the vector elements` } { - equal { ff:: fn { } { ^fail "error message" } ff |disarm |type? } 'error - equal { ff:: fn { } { ^fail "error message" } ff |disarm |message? } "error message" - equal { ff:: fn { } { ^fail 404 } ff |disarm |status? } 404 - equal { ff:: fn { } { ^fail 'user-error } ff |disarm |kind? } 'user-error + equal { cc math , vector [ 1 2 3 4 5 ] |std-deviation |round\to 2 } 1.58 } { } - group "refail" - "Re-raises an existing error with additional context. TODO -- duplicate of check" + group "cosine-similarity" + "Calculates the cosine similarity between two vectors." { argsn 2 - argtypes { - 1 [ Error ] - } + arg `vector1: first vector object` + arg `vector2: second vector object` + returns `decimal representing the cosine similarity between the two vectors` } { + equal { cosine-similarity vector [ 1 0 ] vector [ 0 1 ] } 0.0 + equal { cosine-similarity vector [ 1 1 ] vector [ 1 1 ] } 1.0 } { } - group "failure" - "Creates an error object without setting any flags (unlike fail and ^fail)." + group "correlation" + "Calculates the correlation coefficient between two vectors." { - argsn 1 - arg `error_info: String message, Integer code, or block for multiple parameters` - returns `error object without setting any flags` + argsn 2 + arg `vector1: first vector object` + arg `vector2: second vector object` + returns `decimal representing the correlation coefficient between the two vectors` } { - equal { failure "error message" |type? } 'error - equal { failure "error message" |message? } "error message" - equal { failure 404 |status? } 404 + equal { correlation vector [ 1 2 3 4 5 ] vector [ 1 2 3 4 5 ] } 1.0 + equal { correlation vector [ 1 2 3 4 5 ] vector [ 5 4 3 2 1 ] } -1.0 } { } - group "empty" - "Creates an 404 error object without setting any flags." + group "dot-product" + "Calculates the dot product between two vectors." { - arg `error_info: String message, Integer code, or block for multiple parameters` - returns `error object without setting any flags` + argsn 2 + arg `vector1: first vector object` + arg `vector2: second vector object` + returns `decimal representing the dot product of the two vectors` } { - equal { failure "error message" |type? } 'error - equal { failure "error message" |message? } "error message" - equal { failure 404 |status? } 404 + equal { dot-product vector [ 1 2 3 ] vector [ 4 5 6 ] } 32.0 } { } - group "failure\\wrap" - "Creates a new error that wraps an existing error, allowing for error chaining." + group "euclidean-distance" + "Calculates the Euclidean distance between two vectors. Useful for K-Means clustering." { argsn 2 - argtypes { - 2 [ Error ] - } - arg `error_info: String message, Integer code, or block for multiple parameters` - arg `error: Error object to wrap` - returns `new error object that wraps the provided error` + arg `vector1: first vector object` + arg `vector2: second vector object` + returns `decimal representing the Euclidean distance between the two vectors` } { - equal { failure\wrap "outer error" failure "inner error" |message? } "outer error" - equal { failure\wrap "outer error" failure "inner error" |type? } 'error + equal { euclidean-distance vector [ 0 0 ] vector [ 3 4 ] } 5.0 + equal { euclidean-distance vector [ 1 2 3 ] vector [ 1 2 3 ] } 0.0 } { } - group "is-error" - "Returns true if the value is an error, false otherwise." + group "mean-vectors" + "Calculates the element-wise mean of multiple vectors. Useful for creating 'Master Anchors'." { argsn 1 - arg `value: Any value to check` - returns `boolean true if the value is an error, false otherwise` + arg `block: block of vectors to average` + returns `new vector representing the element-wise mean of all input vectors` } { - equal { is-error failure "test" } true - equal { is-error 123 } false + equal { mean-vectors [ vector [ 1 2 ] vector [ 3 4 ] ] |block } { 2.0 3.0 } } { } - group "error-kind?" - "Returns the kind of an error, or void if not an error." + group "unit-vector" + "Returns a new normalized vector (length = 1). Essential for consistent dot products and overlays." { argsn 1 - arg `value: Any value to check` - returns `the kind of the error as a word, or void if not an error` + arg `vector: vector object to normalize` + returns `new vector with length 1 (unit vector)` } { - equal { error-kind? failure { 'syntax-error 404 "Syntax error" } } 'syntax-error - equal { error-kind? 123 } _ + equal { vector [ 3 4 ] |unit-vector |normalize } 1.0 } { } - group "is-error-of-kind" - "Returns true if the value is an error of the specified kind, false otherwise." + group "project-vector" + "Projects vector onto another vector. Answers: 'How much of this vector is in the direction of another?'" { argsn 2 - argtypes { - 2 [ Word Tagword ] - } - arg `error: Error object to check` - arg `kind: Word or tagword representing the error kind to check against` - returns `boolean true if the error is of the specified kind, false otherwise` + arg `vector1: vector to project` + arg `vector2: vector to project onto` + returns `new vector representing the projection of vector1 onto vector2` } { - equal { is-error-of-kind failure { 'syntax-error 404 "Syntax error" } 'syntax-error } true - equal { is-error-of-kind failure { 'syntax-error 404 "Syntax error" } 'runtime-error } false + equal { vector [ 3 4 ] |project-vector vector [ 1 0 ] |block } { 3.0 0.0 } } { } - group "cause?" - "Extracts the root cause from an error chain by traversing the Parent references." + group "reject-vector" + "Removes projection from vector. Use this to 'subtract' a concept direction from a vector." { - argsn 1 - argtypes { - 1 [ Error ] - } - arg `error: Error object to extract the root cause from` - returns `the root cause error from an error chain` + argsn 2 + arg `vector1: vector to reject from` + arg `vector2: vector representing the direction to remove` + returns `new vector with the projection onto vector2 removed` } { - equal { cause? failure\wrap "outer error" failure "inner error" |message? } "inner error" - equal { cause? failure "single error" |message? } "single error" + equal { vector [ 3 4 ] |reject-vector vector [ 1 0 ] |block } { 0.0 4.0 } } { } - group "status?" - "Extracts the numeric status code from an error object." +} + +section "Matrix " "Matrix operations for 2D numerical data" { + group "matrix\\zeros" + "Creates a new matrix with the given dimensions, initialized to zeros." { - argsn 1 - argtypes { - 1 [ Error ] - } - arg `error: Error object to extract status code from` - returns `integer status code of the error` + argsn 2 + arg `rows: number of rows` + arg `cols: number of columns` + returns `a new zero-initialized matrix` } { - equal { failure 404 |status? } 404 - equal { failure "message" |status? } 0 - error { "not an error" |status? } + equal { matrix\zeros 3 4 |type? } 'matrix + equal { matrix\zeros 2 3 |rows? } 2 + equal { matrix\zeros 2 3 |cols? } 3 } { } - group "message?" - "Extracts the message string from an error object." + group "matrix" + "Creates a matrix from a shape block and a data block." { - argsn 1 - argtypes { - 1 [ Error ] - } - arg `error: Error object to extract message from` - returns `string message of the error` + argsn 2 + arg `shape: block with { rows cols }` + arg `data: block of decimal values in row-major order` + returns `a new matrix with the given data` } { - equal { failure "error message" |message? } "error message" - equal { failure 404 |message? } "" - error { "not an error" |message? } + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-get 1 2 } 6.0 } { } - group "details?" - "Extracts additional details from an error object as a dictionary." + group "matrix\\randn" + "Creates a matrix with random normal values (mean 0, std 1)." { - argsn 1 - argtypes { - 1 [ Error ] - } - arg `error: Error object to extract additional details from` - returns `dictionary containing any additional values stored in the error` + argsn 2 + arg `rows: number of rows` + arg `cols: number of columns` + returns `a new matrix with random normal values (mean 0, std 1)` } { - ; equal { failure { "code" 404 "info" "Not Found" } |details? |type? } 'dict - ; equal { failure { "code" 404 "info" "Not Found" } |details? .code } 404 - error { "not an error" |details? } + equal { matrix\randn 3 4 |rows? } 3 + equal { matrix\randn 3 4 |cols? } 4 } { } - group "has-failed" - "Tests if a value is an error object, returning true for errors and false for non-errors." + group "matrix\\ones" + "Creates a matrix filled with ones." { - argsn 1 - arg `value: Any value to check` - returns `boolean true if the value is an error, false otherwise` + argsn 2 + arg `rows: number of rows` + arg `cols: number of columns` + returns `a new matrix filled with ones` } { - equal { has-failed failure "error" } true - equal { has-failed "not an error" } false - equal { has-failed 123 } false - equal { has-failed try { fail "error" } } true + equal { matrix\ones 2 3 |mat-get 0 0 } 1.0 + equal { matrix\ones 2 3 |mat-get 1 2 } 1.0 } { } - group "is-failure" - "Checks if a value is a failure/error type. Unlike has-failed, this doesn't accept failures - the value must already be disarmed." + group "matrix\\eye" + "Creates an identity matrix of size n×n." { argsn 1 - arg `value: Any value to check (must already be disarmed if it was a failure)` - returns `boolean true if the value is an error type, false otherwise` + arg `n: size of the identity matrix` + returns `an n×n identity matrix` } { - equal { try { fail "error" } |disarm |is-failure } true - equal { failure "error" |is-failure } true - equal { 123 |is-failure } false - equal { "hello" |is-failure } false + equal { matrix\eye 3 |mat-get 0 0 } 1.0 + equal { matrix\eye 3 |mat-get 0 1 } 0.0 + equal { matrix\eye 3 |mat-get 1 1 } 1.0 } { } - group "is-success" - "Returns true for any value that is not a failure/error type. The opposite of is-failure." + group "shape?" + "Returns the shape of a matrix as { rows cols }." { argsn 1 - arg `value: Any value to check (must already be disarmed if it was a failure)` - returns `boolean true if the value is not an error type, false if it is an error` + arg `mat: matrix` + returns `block with { rows cols }` } { - equal { 123 |is-success } true - equal { "hello" |is-success } true - equal { { 1 2 3 } |is-success } true - equal { failure "error" |is-success } false - equal { try { fail "error" } |disarm |is-success } false + equal { matrix\zeros 3 4 |shape? } { 3 4 } } { } - group "disarm" - "Clears the failure flag while preserving the error object, allowing error inspection without propagation." + group "rows?" + "Returns the number of rows in a matrix." { argsn 1 - arg `error: Error object to disarm` - returns `the original error object, but clears the failure flag` + arg `mat: matrix` + returns `number of rows` } { - equal { try { fail "error" |disarm } |type? } 'error - equal { try { fail "error" |disarm |message? } } "error" - equal { try { fail "error" } |has-failed } true - equal { try { fail "error" |disarm } |has-failed } true + equal { matrix\zeros 3 4 |rows? } 3 } { } - group "check" - "Checks if a value is in failure state and wraps it with a new error if so, otherwise returns the original value." + group "cols?" + "Returns the number of columns in a matrix." { - argsn 2 - arg `value: Value to check for failure state` - arg `error_info: Error information to use if value is in failure state` - returns `original value if not in failure state, or a new error wrapping the original error` + argsn 1 + arg `mat: matrix` + returns `number of columns` } { - equal { 5 |check "Value must be positive" } 5 - equal { try { fail "Original error" |check "Wrapped error" } |message? } "Wrapped error" - equal { try { fail "Original error" |check "Wrapped error" } |details? |type? } 'dict + equal { matrix\zeros 3 4 |cols? } 4 } { } - group "^check" - "Like 'check' but also sets the return flag to immediately exit the current function." + group "mat-get" + "Gets the element at the specified row and column (0-indexed)." { - argsn 2 - arg `value: Value to check for failure state` - arg `error_info: Error information to use if value is in failure state` - returns `original value if not in failure state, or immediately returns from function with a new error` + argsn 3 + arg `mat: matrix` + arg `row: row index (0-based)` + arg `col: column index (0-based)` + returns `the element at (row, col)` } { - equal { fn { x } { x |^check "Error in function" } |apply [ 5 ] } 5 - equal { ff:: fn { x } { fail "Original" |^check "Wrapped" } ff 5 |disarm |message? } "Wrapped" + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-get 0 1 } 2.0 + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-get 1 0 } 4.0 } { } - group "^ensure" - "Checks if a value is truthy and returns it if so, otherwise creates an error and immediately returns from the function." + group "mat-set!" + "Sets the element at the specified row and column (0-indexed). Modifies matrix in place." { - argsn 2 - arg `condition: Value to test for truthiness` - arg `error_info: Error information to use if condition is not truthy` - returns `condition value if truthy, or immediately returns from function with an error` + argsn 4 + arg `mat: matrix (modified in place)` + arg `row: row index (0-based)` + arg `col: column index (0-based)` + arg `val: value to set` + returns `the modified matrix` } { - equal { fn { x } { x |^ensure "Must be positive" } |apply [ 5 ] } true - equal { ff:: fn { x } { x > 0 |^ensure "Must be positive" } ff -1 |disarm |message? } "Must be positive" + equal { m: matrix\zeros 2 2 , mat-set! m 0 1 5.0 , mat-get m 0 1 } 5.0 } { } - group "ensure" - "Checks if a value is truthy and returns it if so, otherwise creates an error with the failure flag set." + group "mat-row" + "Returns a row of the matrix as a vector." { argsn 2 - arg `condition: Value to test for truthiness` - arg `error_info: Error information to use if condition is not truthy` - returns `condition value if truthy, or creates an error with failure flag set` + arg `mat: matrix` + arg `row: row index (0-based)` + returns `the row as a vector` } { - equal { 5 > 0 |ensure "Must be positive" } true - equal { try { -1 > 0 |ensure "Must be positive" } |message? } "Must be positive" + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-row 0 |type? } 'vector } { } - group "requires-one-of" - "Validates that a value matches one of the provided options, failing with a descriptive error if not." + group "mat-col" + "Returns a column of the matrix as a vector." { argsn 2 - argtypes { - 2 [ Block ] - } - arg `value: Value to check against valid options` - arg `options: Block containing valid values` - returns `original value if it matches one of the options, or creates an error with failure flag set` + arg `mat: matrix` + arg `col: column index (0-based)` + returns `the column as a vector` } { - equal { "a" |requires-one-of { "a" "b" "c" } } "a" - equal { "b" |requires-one-of { "a" "b" "c" } } "b" - equal { try { "x" |requires-one-of { "a" "b" "c" } } |message? |contains "must be one of" } true - equal { try { "x" |requires-one-of { "a" "b" "c" } } |message? |contains "[String: x]" } true - equal { try { 5 |requires-one-of { 1 2 3 } } |message? |contains "5" } true + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-col 1 |type? } 'vector } { } - group "^requires-one-of" - "Validates that a value matches one of the provided options, failing with a descriptive error if not." + group "mat-transpose" + "Returns the transpose of a matrix." { - argsn 2 - argtypes { - 2 [ Block ] - } - arg `value: Value to check against valid options` - arg `options: Block containing valid values` - returns `original value if it matches one of the options, or creates an error with failure flag set` + argsn 1 + arg `mat: matrix` + returns `transposed matrix` } { - equal { "a" |requires-one-of { "a" "b" "c" } } "a" - equal { "b" |requires-one-of { "a" "b" "c" } } "b" - equal { try { "x" |requires-one-of { "a" "b" "c" } } |message? |contains "must be one of" } true - equal { try { "x" |requires-one-of { "a" "b" "c" } } |message? |contains "[String: x]" } true - equal { try { 5 |requires-one-of { 1 2 3 } } |message? |contains "5" } true + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-transpose |shape? } { 3 2 } + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-transpose |mat-get 0 1 } 4.0 } { } - group "ensure\\with" - "Injects a value into a validation block and returns the value if the block is truthy, otherwise creates an error." + group "mat-mul" + "Matrix multiplication. A (m×n) × B (n×p) = C (m×p)." { - argsn 3 - argtypes { - 2 [ Block ] - } - arg `value: Value to inject into the validation block` - arg `block: Block that receives the value and returns truthy/falsy result` - arg `error_info: Error information to use if block returns falsy` - returns `original value if block returns truthy, or creates an error with failure flag set` + argsn 2 + arg `A: left matrix (m×n)` + arg `B: right matrix (n×p)` + returns `result matrix (m×p)` } { - equal { dict { "status" 200 } |ensure\with { -> "status" = 200 } "status check failed" } dict { "status" 200 } - equal { try { dict { "status" 404 } |ensure\with { -> "status" = 200 } "status check failed" } |message? } "status check failed" + equal { A: matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } , B: matrix { 3 2 } { 1.0 2.0 3.0 4.0 5.0 6.0 } , mat-mul A B |shape? } { 2 2 } + equal { A: matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } , B: matrix { 3 2 } { 1.0 2.0 3.0 4.0 5.0 6.0 } , mat-mul A B |mat-get 0 0 } 22.0 } { } - group "^ensure\\with" - "Like ensure\\with but also sets the return flag to immediately exit the current function." + group "mat-hadamard" + "Element-wise (Hadamard) multiplication of two matrices. In the future, could have a dedicated .* operator." { - argsn 3 - argtypes { - 2 [ Block ] - } - arg `value: Value to inject into the validation block` - arg `block: Block that receives the value and returns truthy/falsy result` - arg `error_info: Error information to use if block returns falsy` - returns `original value if block returns truthy, or immediately returns from function with an error` + argsn 2 + arg `A: first matrix` + arg `B: second matrix (same dimensions)` + returns `element-wise product` } { - ; equal { fn { req } { req |^ensure\with { .Status? = 200 } "request failed" } |apply [ Request "GET" "http://example.com" ] } Request "GET" "http://example.com" - equal { ff:: fn { val } { val |^ensure\with { -> "status" = 200 } "status check failed" } ff dict { "status" 404 } |disarm |message? } "status check failed" + equal { A: matrix\ones 2 2 , B: matrix\ones 2 2 , mat-hadamard A B |mat-get 0 0 } 1.0 } { } - group "fix" - "Handles errors by executing a block if the value is in failure state, clearing the failure flag." + group "mat-add" + "Element-wise addition of two matrices. In the future, could have a dedicated .+ operator." { argsn 2 - argtypes { - 2 [ Block ] - } - arg `value: Value to check for failure state` - arg `handler: Block to execute if value is in failure state` - returns `original value if not in failure state, or result of executing the handler block` + arg `A: first matrix` + arg `B: second matrix (same dimensions)` + returns `element-wise sum` } { - equal { 5 |fix { + 10 } } 5 - equal { try { fail "error" |fix { "fixed" } } } "fixed" - equal { try { fail "error" |fix { fail "new error" } } |message? } "new error" + equal { A: matrix\ones 2 2 , B: matrix\ones 2 2 , mat-add A B |mat-get 0 0 } 2.0 } { } - group "^fix" - "Like 'fix' but also sets the return flag to immediately exit the current function with the handler result." + group "mat-sub" + "Element-wise subtraction of two matrices. In the future, could have a dedicated .- operator." { argsn 2 - argtypes { - 2 [ Block ] - } - arg `value: Value to check for failure state` - arg `handler: Block to execute if value is in failure state` - returns `original value if not in failure state, or immediately returns from function with handler result` + arg `A: first matrix` + arg `B: second matrix (same dimensions)` + returns `element-wise difference` } { - equal { fn { x } { x |^fix { "fixed" } } |apply [ 5 ] } 5 - equal { ff:: fn { x } { fail "error" |^fix { "fixed" } } ff 5 } "fixed" + equal { A: matrix\ones 2 2 , B: matrix\ones 2 2 , mat-sub A B |mat-get 0 0 } 0.0 } { } - group "fix\\either" - "Executes one of two blocks depending on whether the value is in failure state." + group "mat-scale" + "Multiplies all elements of a matrix by a scalar." { - argsn 3 - argtypes { - 2 [ Block ] - 3 [ Block ] - } - arg `value: Value to check for failure state` - arg `error_handler: Block to execute if value is in failure state` - arg `success_handler: Block to execute if value is not in failure state` - returns `result of executing the appropriate handler block` + argsn 2 + arg `mat: matrix` + arg `scalar: number to multiply by` + returns `scaled matrix` } { - equal { fix\either failure "error" { "fixed" } { "not fixed" } } "fixed" - equal { fix\either 5 { "fixed" } { "not fixed" } } "not fixed" + equal { matrix\ones 2 2 |mat-scale 3.0 |mat-get 0 0 } 3.0 } { } - group "fix\\else" - "Executes a block if the value is not in failure state, otherwise returns the original value." + group "mat-sum-rows" + "Returns a vector containing the sum of each row." { - argsn 2 - argtypes { - 2 [ Block ] - } - arg `value: Value to check for failure state` - arg `success_handler: Block to execute if value is not in failure state` - returns `result of executing the success handler if value is not in failure state, or the original value` + argsn 1 + arg `mat: matrix` + returns `vector with sum of each row` } { - equal { 5 |fix\else { "not fixed" } } "not fixed" - equal { try { fail "error" |fix\else { "not fixed" } } |message? } "error" + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-sum-rows |type? } 'vector } { } - group "fix\\continue" - "Executes one of two blocks depending on whether the value is in failure state, like an error-handling if/else." + group "mat-sum-cols" + "Returns a vector containing the sum of each column." { - argsn 3 - argtypes { - 2 [ Block ] - 3 [ Block ] - } - arg `value: Value to check for failure state` - arg `error_handler: Block to execute if value is in failure state` - arg `success_handler: Block to execute if value is not in failure state` - returns `result of executing the appropriate handler block` + argsn 1 + arg `mat: matrix` + returns `vector with sum of each column` } { - equal { 5 |fix\continue { "error handler" } { "success handler" } } "success handler" - equal { try { fail "error" |fix\continue { "error handler" } { "success handler" } } } "error handler" + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-sum-cols |type? } 'vector } { } - group "continue" - "Executes a block only if the value is not in failure state, opposite of 'fix'." + group "mat-max-rows" + "Returns a vector containing the maximum of each row." { - argsn 2 - argtypes { - 2 [ Block ] - } - arg `value: Value to check for failure state` - arg `block: Block to execute if value is not in failure state` - returns `result of executing the block if value is not in failure state, or the original value` + argsn 1 + arg `mat: matrix` + returns `vector with max of each row` } { - equal { 5 |continue { + 10 } } 15 - equal { try { fail "error" |continue { + 10 } } } failure "error" + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-max-rows |type? } 'vector } { } - group "^fix\\match" - "Error handling switch that matches error codes with handler blocks and sets the return flag." + group "mat-max-cols" + "Returns a vector containing the maximum of each column." { - argsn 2 - argtypes { - 2 [ Block ] - } - arg `error: Error object to match against` - arg `cases: Block containing error codes and corresponding handler blocks` - returns `result of executing the matching handler block, or the original error if no match` + argsn 1 + arg `mat: matrix` + returns `vector with max of each column` } { - equal { ^fix\match failure 404 { 404 { "Not Found" } 500 { "Server Error" } } } "Not Found" - equal { ^fix\match failure 500 { 404 { "Not Found" } 500 { "Server Error" } } } "Server Error" - equal { ^fix\match failure 403 { 404 { "Not Found" } 500 { "Server Error" } _ { "Unknown Error" } } } "Unknown Error" + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-max-cols |type? } 'vector } { } - group "try" - "Takes a block of code and does (runs) it." + group "mat-to-block" + "Converts a matrix to a block of blocks (each row is a block)." { argsn 1 - pure - argtypes { - 1 [ Block ] - } + arg `mat: matrix` + returns `block of blocks (each inner block is a row)` } { - equal { try { 123 + 123 } } 246 - equal { try { 123 + "asd" } |type? } 'error - equal { try { 123 + } |type? } 'error - equal { try { fail "oops" } |message? } "oops" + equal { matrix { 2 2 } { 1.0 2.0 3.0 4.0 } |mat-to-block |length? } 2 } { } - group "try-all" - "Executes a block and returns a result tuple [success, result], where success is true if no error occurred." + group "block-to-mat" + "Converts a block of blocks (rows) to a matrix." { argsn 1 - pure - argtypes { - 1 [ Block ] - } - arg `block: Block of code to execute` - returns `a block containing [success, result], where success is true if no error occurred` + arg `block: block of blocks (each inner block is a row)` + returns `matrix` } { - equal { try-all { 1 + 2 } } [ true 3 ] - equal { try-all { fail "custom error" } |first } false + equal { { { 1.0 2.0 } { 3.0 4.0 } } |block-to-mat |shape? } { 2 2 } } { } - group "try\\in" - "Takes a Context and a Block. It Does a block inside a given Context." + group "vector-to-mat" + "Converts a vector to a matrix. Use 'row for 1×n or 'col for n×1." { argsn 2 - argtypes { - 1 [ Context ] - 2 [ Block ] - } + arg `vec: vector` + arg `orientation: 'row or 'col` + returns `matrix (1×n or n×1)` } { - equal { c: context { x: 100 } try\in c { x * 9.99 } } 999.0 - equal { c: context { x:: 100 } try\in c { inc! 'x } } 101 - equal { c: context { x:: 100 } try\in c { x:: 200 , x } } 200 - equal { c: context { x:: 100 } try\in c { x:: 200 } c/x } 200 - equal { c: context { x: 100 } try\in c { inc! 'y } |type? } 'error + equal { vector { 1.0 2.0 3.0 } |vector-to-mat 'row |shape? } { 1 3 } + equal { vector { 1.0 2.0 3.0 } |vector-to-mat 'col |shape? } { 3 1 } } { } - group "finally" - "Executes a block and ensures another block is executed afterward, regardless of errors." + group "mat-slice" + "Extracts a sub-matrix. Row and column ranges are { start end } (inclusive, 0-indexed)." { - argsn 2 - argtypes { - 1 [ Block ] - 2 [ Block ] - } - arg `main-block: Block of code to execute` - arg `finally-block: Block to execute afterward, regardless of errors` - returns `result of the main block, preserving any failure state` + argsn 3 + arg `mat: matrix` + arg `row-range: { start end } (inclusive)` + arg `col-range: { start end } (inclusive)` + returns `sub-matrix` } { - equal { finally { 1 + 2 } { print "cleanup" } } 3 - equal { try { finally { fail "error" } { print "cleanup" } } |message? } "error" + equal { matrix { 2 3 } { 1.0 2.0 3.0 4.0 5.0 6.0 } |mat-slice { 0 1 } { 1 2 } |shape? } { 2 2 } } { } - group "retry" - "Executes a block and retries it up to N times if it results in a failure." + group "mat-softmax" + "Applies softmax to each column of the matrix. Returns a new matrix." { - argsn 2 - argtypes { - 1 [ Integer ] - 2 [ Block ] - } - arg `retries: Integer number of retries to attempt` - arg `block: Block of code to execute and potentially retry` - returns `result of the block if successful, or the last failure if all retries fail` + argsn 1 + arg `matrix: input matrix` + returns `a new matrix with softmax applied to each column` } { - ; equal { retry 3 { fail 101 } |disarm |type? } 'error - ; equal { retry 3 { fail 101 } |disarm |status? } 101 - equal { retry 3 { 10 + 1 } } 11 + equal { matrix { 2 2 } { 1.0 2.0 3.0 4.0 } |mat-softmax |mat-get 0 0 |> 0.1 } true } { } - group "persist" - "Executes a block repeatedly until it succeeds (no failure), then returns the successful result. Gives up after 1000 attempts." + group "mat-set-col!" + "Sets a column of the matrix from a vector or block. Mutates in place." { - argsn 1 - argtypes { - 1 [ Block ] - } - arg `block: Block of code to execute repeatedly until it succeeds` - returns `result of the block when it finally succeeds (no failure), or error if 1000 attempts exceeded` + argsn 3 + arg `matrix: matrix to modify` + arg `col: column index (0-based)` + arg `values: vector or block of values` + returns `the modified matrix` } { - equal { persist { 10 + 1 } } 11 - ; equal { counter:: 0 persist { counter:: counter + 1 , either counter > 3 { counter } { fail "not ready" } } } 4 - error { persist { fail "always fails" } } + equal { m: matrix\zeros 3 2 , mat-set-col! m 0 vector { 1.0 2.0 3.0 } , mat-get m 1 0 } 2.0 } { } - group "timeout" - "Executes a block of code with a timeout, failing if execution exceeds the specified duration in milliseconds." + group "vec-add" + "Adds two vectors element-wise. Returns a new vector." { argsn 2 - argtypes { - 1 [ Integer ] - 2 [ Block ] - } - arg `ms: Integer timeout duration in milliseconds` - arg `block: Block of code to execute with a timeout` - returns `result of the block if it completes within the timeout, or a timeout error` + arg `v1: first vector` + arg `v2: second vector` + returns `a new vector with element-wise sum` } { - equal { timeout 5000 { "ok" } } "ok" - equal { try { timeout 100 { sleep 1000 , "ok" } } |message? |contains "timed out" } true + equal { vec-add vector { 1.0 2.0 } vector { 3.0 4.0 } |first } 4.0 } { } -} + group "vec-sub" + "Subtracts two vectors element-wise (v1 - v2). Returns a new vector." + { + argsn 2 + arg `v1: first vector` + arg `v2: second vector` + returns `a new vector with element-wise difference (v1 - v2)` + } -section "Fast Math " "Fast infix math evaluator" { - group "mth" - "Fast infix math evaluator with proper operator precedence (+,-,*,/,//,%,<,>,=,<=,>=,!=). Single-pass Shunting-yard, direct Go arithmetic, no builtin lookup." + { + equal { vec-sub vector { 5.0 3.0 } vector { 1.0 2.0 } |first } 4.0 + } + + { + } + + group "vec-to-block" + "Converts a vector to a block of decimal values." { argsn 1 - pure - argtypes { - 1 [ Block ] - } - arg `block: Block containing an infix math expression` - returns `result of evaluating the expression with proper operator precedence` + arg `v: vector to convert` + returns `a block containing the vector values as decimals` } { - equal { mth { 1 + 2 } } 3 - equal { mth { 2 + 3 * 4 } } 14 - equal { mth { 10 - 5 - 2 } } 3 - equal { mth { 8 // 3 } } 2 - equal { mth { 8 % 3 } } 2 - equal { mth { 5 + 5 < 10 } } false - equal { mth { 5 + 5 <= 10 } } true - equal { mth { 5 + 4 < 10 } } true - equal { mth { 1 + 2 * 3 } } 7 - equal { a: 5 mth { a * 2 } } 10 - equal { a: 3 b: 4 mth { a * a + b * b } } 25 + equal { vec-to-block vector { 1.0 2.0 3.0 } |second } 2.0 } { diff --git a/tests/crypto.info.rye b/tests/crypto.info.rye index 44b8b54c..3dc1dd1a 100644 --- a/tests/crypto.info.rye +++ b/tests/crypto.info.rye @@ -640,10 +640,12 @@ section "Bcrypt " "Functions for bcrypt password hashing and token generation" { { argsn 1 arg `password: String to hash` - returns `string containing the bcrypt hash of the password` + arg `password: String to hash` + returns `string containing the bcrypt hash of the passwordstring containing the bcrypt hash of the password` } { + equal { bcrypt-hash "secret" |type? } 'string } { @@ -655,10 +657,14 @@ section "Bcrypt " "Functions for bcrypt password hashing and token generation" { argsn 2 arg `hash: String containing the bcrypt hash` arg `password: Plain text password to compare` - returns `integer 1 if the password matches the hash, 0 otherwise` + arg `hash: String containing the bcrypt hash` + arg `password: Plain text password to compare` + returns `integer 1 if the password matches the hash, 0 otherwiseinteger 1 if the password matches the hash, 0 otherwise` } { + equal { bcrypt-check "$2a$10$N9qo8uLOickgx2ZMRZo4e.PFTlCOKQOLawQy5AdYyZuG7JC2rA9e." "password" } 1 + equal { bcrypt-check "$2a$10$N9qo8uLOickgx2ZMRZo4e.PFTlCOKQOLawQy5AdYyZuG7JC2rA9e." "wrong" } 0 } { @@ -669,10 +675,13 @@ section "Bcrypt " "Functions for bcrypt password hashing and token generation" { { argsn 1 arg `length: Integer number of random bytes to generate` - returns `string containing the hex-encoded random token` + arg `length: Integer number of random bytes to generate` + returns `string containing the hex-encoded random tokenstring containing the hex-encoded random token` } { + equal { generate-token 4 |length? } 8 + error { generate-token "x" } } { diff --git a/tests/formats.info.rye b/tests/formats.info.rye index a8b273b6..6de49550 100644 --- a/tests/formats.info.rye +++ b/tests/formats.info.rye @@ -15,6 +15,22 @@ section "Regexp " "Go like Regular expressions" { { } + group "re" + "Creates a compiled regular expression object from a pattern string." + { + argsn 1 + arg `pattern: String containing a regular expression pattern` + returns `native regexp object or error if pattern is invalid` + } + + { + equal { re "[0-9]" |type? } 'native + equal { re "[0-9]" |kind? } 'regexp + } + + { + } + group "regexp//Is-match" "Tests if a string matches the regular expression pattern." { @@ -465,6 +481,128 @@ section "Markdown " "Functions for processing and converting Markdown documents" { } + group "markdown->html" + "Converts Markdown text to HTML." + { + argsn 1 + arg `text: Markdown source string` + returns `string containing HTML` + } + + { + equal { markdown->html "# Title" |contains "html 123 } + } + + { + } + + group "markdown" + "Creates a Markdown value from a string." + { + argsn 1 + arg `text: String or Markdown value` + returns `Markdown value` + } + + { + equal { markdown "Hello" |type? } 'markdown + ; equal { markdown markdown "Hello" |type? } 'markdown + } + + { + } + + group "markdown//text" + "Gets the raw markdown text from a Markdown value." + { + argsn 1 + arg `md: Markdown value` + returns `raw markdown text as string` + } + + { + equal { markdown "Hello" |markdown//text } "Hello" + } + + { + } + + group "markdown//length" + "Gets the length of the markdown text in characters." + { + argsn 1 + arg `md: Markdown value` + returns `integer length in characters` + } + + { + equal { markdown "Hello" |markdown//length } 5 + } + + { + } + + group "markdown//to-html" + "Converts a Markdown value to HTML." + { + argsn 1 + arg `md: Markdown value` + returns `string containing HTML` + } + + { + equal { markdown "# T" |markdown//to-html |contains "/dev/null + +# BASE (core language builtins) ../cmd/rbit/rbit ../evaldo/builtins_base_boolean.go > base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_numbers.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_strings.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_time.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_collections.go >> base.info.rye -../cmd/rbit/rbit ../batteries/builtins_vector.go >> base.info.rye -../cmd/rbit/rbit ../batteries/builtins_matrix.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_types.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_contexts.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_conditionals.go >> base.info.rye @@ -13,9 +24,52 @@ ../cmd/rbit/rbit ../evaldo/builtins.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_combinators.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_printing_not.go >> base.info.rye -../cmd/rbit/rbit ../baseio/builtins_printing.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_error_handling.go >> base.info.rye ../cmd/rbit/rbit ../evaldo/builtins_base_mth.go >> base.info.rye +# Some math helpers that feel core +../cmd/rbit/rbit ../batteries/builtins_vector.go >> base.info.rye +../cmd/rbit/rbit ../batteries/builtins_matrix.go >> base.info.rye + +# BASEIO (IO-related builtins and protocols doing IO) +../cmd/rbit/rbit ../baseio/builtins_printing.go > baseio.info.rye +../cmd/rbit/rbit ../baseio/builtins_baseio.go >> baseio.info.rye +../cmd/rbit/rbit ../batteries/builtins_io.go >> baseio.info.rye +../cmd/rbit/rbit ../batteries/builtins_cmd.go >> baseio.info.rye +../cmd/rbit/rbit ../batteries/builtins_os.go >> baseio.info.rye +../cmd/rbit/rbit ../batteries/builtins_http.go >> baseio.info.rye +../cmd/rbit/rbit ../batteries/builtins_email.go >> baseio.info.rye +../cmd/rbit/rbit ../batteries/builtins_mail.go >> baseio.info.rye +# ../cmd/rbit/rbit ../batteries/builtins_imap.go >> baseio.info.rye +../cmd/rbit/rbit ../batteries/builtins_smtpd.go >> baseio.info.rye +../cmd/rbit/rbit ../batteries/builtins_mqtt.go >> baseio.info.rye + +# BATTERIES (all the rest) +../cmd/rbit/rbit ../evaldo/builtins_table.go > batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_regexp.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_json.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_bson.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_sxml.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_html.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_markdown.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_conversion.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_encoding.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_sqlite.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_psql.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_mysql.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_crypto.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_bcrypt.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_math.go >> batteries.info.rye +../cmd/rbit/rbit ../evaldo/builtins_validation.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_match.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_eyr.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_ssh.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_goroutines.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_complex.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_pipes.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_structures.go >> batteries.info.rye +../cmd/rbit/rbit ../batteries/builtins_web.go >> batteries.info.rye + +# Keep generating per-topic .info.rye for existing individual pages (unchanged below) ../cmd/rbit/rbit ../evaldo/builtins_table.go > table.info.rye ../cmd/rbit/rbit ../batteries/builtins_regexp.go > formats.info.rye ../cmd/rbit/rbit ../batteries/builtins_json.go >> formats.info.rye