evaldo

package
v0.2.61 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 15, 2026 License: BSD-3-Clause Imports: 25 Imported by: 3

Documentation

Overview

builtins_error_handling.go

evaldo_rye0.go - A simplified interpreter for the Rye language

evaldo_rye00.go - A highly simplified interpreter for the Rye language This version only handles builtins and integers for performance optimization experiments

Index

Constants

This section is empty.

Variables

View Source
var BatteryConvertHook func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object) env.Object = func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object) env.Object {
	return MakeBuiltinError(ps, "Conversion requires the batteries package (call batteries.RegisterBatteries first).", "convert")
}

BatteryConvertHook is called by base builtins that perform Kind-based conversion. Batteries set this to batteries.BuiConvert.

View Source
var BatteryDialectMathHook func(ps *env.ProgramState, arg0 env.Object) env.Object = func(ps *env.ProgramState, arg0 env.Object) env.Object {
	return MakeBuiltinError(ps, "Math dialect requires the batteries package.", "math")
}

BatteryDialectMathHook is called by the REPL when the dialect is "math". Batteries set this to DialectMath.

View Source
var BatteryEvalInPersistentCtxHook func(ps *env.ProgramState, ctx PersistentCtxInterface) = func(ps *env.ProgramState, ctx PersistentCtxInterface) {

}

BatteryEvalInPersistentCtxHook is called by "do\inside" when the context is a PersistentCtx. Batteries set this to their EvalBlockInPersistentCtx.

View Source
var BatteryEyrEvalBlockHook func(ps *env.ProgramState, full bool) = func(ps *env.ProgramState, full bool) {

}

BatteryEyrEvalBlockHook is called by the REPL when the dialect is "eyr". Batteries set this to Eyr_EvalBlock.

View Source
var BatteryEyrEvalBlockInsideHook func(ps *env.ProgramState, inj env.Object, injnow bool) = func(ps *env.ProgramState, inj env.Object, injnow bool) {

}

BatteryEyrEvalBlockInsideHook is called by EvalBlockInj when the dialect is EyrDialect. Batteries set this to Eyr_EvalBlockInside.

View Source
var BatteryIsScenarioHook func(ps *env.ProgramState) bool = func(ps *env.ProgramState) bool { return false }

BatteryIsScenarioHook lets batteries signal that scenario/simulation mode is active. Default is false.

View Source
var BatteryMarkdownDisplayHook func(source string) []interface{} = func(source string) []interface{} {
	return nil
}

BatteryMarkdownDisplayHook is called by the base printing builtins to render markdown blocks in the REPL. Batteries set this to their markdown renderer. The returned []interface{} should be the converted display items.

View Source
var BatteryScenarioCaptureOutputHook func(ps *env.ProgramState, payload env.Object) bool = func(ps *env.ProgramState, payload env.Object) bool { return false }

BatteryScenarioCaptureOutputHook lets batteries capture output intents in scenario mode. Return true to indicate capture/handling succeeded (advisory), false otherwise.

View Source
var BatteryScenarioGetHook func(ps *env.ProgramState, name string) (bool, env.Object) = func(ps *env.ProgramState, name string) (bool, env.Object) {
	return false, nil
}

BatteryScenarioGetHook lets batteries (or a scenario runner) provide mocked values for declared input intents. It returns (found, value). Default is no-op.

View Source
var BatteryValidateHook func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object) env.Object = func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object) env.Object {

	return BuiValidate(ps, arg0, arg1)
}

BatteryValidateHook is called by base builtins (e.g. "_<<", "assure-kind") that need to validate a Dict/RyeCtx against a spec block. Batteries set this to batteries.BuiValidate.

View Source
var BuiltinNames map[string]int // TODO --- this looks like some hanging global ... it should move to ProgramState, it doesn't even really work with contrib and external probably
View Source
var Builtins_error_creation = ErrorCreationBuiltins

Export the error handling builtins for registration

View Source
var Builtins_error_handling = ErrorHandlingBuiltins
View Source
var Builtins_error_inspection = ErrorInspectionBuiltins
View Source
var Builtins_table = map[string]*env.Builtin{

	"table": {
		Argsn: 2,
		Doc:   "Creates a table by accepting block of column names and flat block of values",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch header1 := arg0.(type) {
			case env.Block:
				hlen := header1.Series.Len()
				cols, err := ColNames(ps, header1, "table")
				if err != nil {
					return err
				}
				spr := env.NewTable(cols)
				switch data1 := arg1.(type) {
				case env.Block:
					rdata := data1.Series.S

					if hlen > 0 {
						for i := 0; i < len(rdata)/hlen; i++ {
							rowd := make([]any, hlen)
							for ii := 0; ii < hlen; ii++ {
								rowd[ii] = rdata[i*hlen+ii]
							}
							spr.AddRow(*env.NewTableRow(rowd, spr))
						}
					}
					return *spr
				case env.List:
					rdata := data1.Data
					for i := 0; i < len(rdata)/hlen; i++ {
						rowd := make([]any, hlen)
						for ii := 0; ii < hlen; ii++ {
							rowd[ii] = rdata[i*hlen+ii]
						}
						spr.AddRow(*env.NewTableRow(rowd, spr))
					}
					return *spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "table")
				}

			default:
				return MakeArgError(ps, 1, []env.Type{env.BlockType}, "table")
			}
		},
	},

	"table\\columns": {
		Argsn: 2,
		Doc:   "Creats a table by accepting a block of columns",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			return SheetFromColumns(ps, arg0, arg1)
		},
	},

	"table\\rows": {
		Argsn: 2,
		Doc:   "Creates a table by accepting a block or list of rows",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			cols, err := ColNames(ps, arg0, "table\\rows	")
			if err != nil {
				return err
			}
			spr := env.NewTable(cols)
			switch rows := arg1.(type) {
			case env.Block:
				for _, objRow := range rows.Series.S {
					row, err := TableRowsFromBlockOrList(ps, spr, len(cols), objRow)
					if err != nil {
						return err
					}
					spr.AddRow(*row)
				}
				return *spr
			case env.List:
				for _, listRow := range rows.Data {
					row, err := TableRowsFromBlockOrList(ps, spr, len(cols), listRow)
					if err != nil {
						return err
					}
					spr.AddRow(*row)
				}
				return *spr
			default:
				return MakeArgError(ps, 2, []env.Type{env.BlockType, env.ListType}, "table\\rows")
			}
		},
	},

	"to-table": {
		Argsn: 1,
		Doc:   "Creates a table by accepting block or list of dicts",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			switch block := arg0.(type) {
			case env.Block:
				data := block.Series
				if data.Len() == 0 {
					return MakeBuiltinError(ps, "Block is empty", "to-table")
				}
				k := make(map[string]struct{})
				for _, obj := range data.S {
					switch dict := obj.(type) {
					case env.Dict:
						for key := range dict.Data {
							k[key] = struct{}{}
						}
					default:
						return MakeBuiltinError(ps, "Block must contain only dicts", "to-table")
					}
				}
				var keys []string
				for key := range k {
					keys = append(keys, key)
				}
				spr := env.NewTable(keys)
				for _, obj := range data.S {
					switch dict := obj.(type) {
					case env.Dict:
						row := make([]any, len(keys))
						for i, key := range keys {
							data, ok := dict.Data[key]
							if !ok {
								data = env.Void{}
							}
							row[i] = data
						}
						spr.AddRow(*env.NewTableRow(row, spr))
					}
				}
				return *spr

			case env.List:
				data := block.Data
				if len(data) == 0 {
					return MakeBuiltinError(ps, "List is empty", "to-table")
				}
				k := make(map[string]struct{})
				for _, obj := range data {
					switch dict := obj.(type) {
					case map[string]any:
						for key := range dict {
							k[key] = struct{}{}
						}
					case env.Dict:
						for key := range dict.Data {
							k[key] = struct{}{}
						}
					default:
						return MakeBuiltinError(ps, "List must contain only dicts", "to-table")
					}
				}
				var keys []string
				for key := range k {
					keys = append(keys, key)
				}
				spr := env.NewTable(keys)
				for _, obj := range data {
					row := make([]any, len(keys))
					switch dict := obj.(type) {
					case map[string]any:
						for i, key := range keys {
							data, ok := dict[key]
							if !ok {
								data = env.Void{}
							}
							row[i] = env.ToRyeValue(data)
						}
						spr.AddRow(*env.NewTableRow(row, spr))
					case env.Dict:
						row := make([]any, len(keys))
						for i, key := range keys {
							data, ok := dict.Data[key]
							if !ok {
								data = env.Void{}
							}
							row[i] = env.ToRyeValue(data)
						}
						spr.AddRow(*env.NewTableRow(row, spr))
					}
				}
				return *spr

			default:
				return MakeArgError(ps, 1, []env.Type{env.BlockType, env.ListType}, "to-table")
			}
		},
	},

	"to-dict": {
		Argsn: 1,
		Doc:   "Creates a dict from a two-column table, using the first column as keys and the second column as values.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch s := arg0.(type) {
			case env.Table:
				spr = &s
			case *env.Table:
				spr = s
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "to-dict")
			}
			if len(spr.Cols) != 2 {
				return MakeBuiltinError(ps, fmt.Sprintf("to-dict requires a table with exactly 2 columns, got %d", len(spr.Cols)), "to-dict")
			}
			data := make(map[string]any, len(spr.Rows))
			for _, row := range spr.Rows {
				keyObj := env.ToRyeValue(row.Values[0])
				var keyStr string
				switch k := keyObj.(type) {
				case env.String:
					keyStr = k.Value
				case env.Word:
					keyStr = ps.Idx.GetWord(k.Index)
				case env.Integer:
					keyStr = fmt.Sprintf("%d", k.Value)
				case env.Decimal:
					keyStr = fmt.Sprintf("%g", k.Value)
				default:
					return MakeBuiltinError(ps, fmt.Sprintf("to-dict: key column values must be strings or numbers, got %s", keyObj.Inspect(*ps.Idx)), "to-dict")
				}
				data[keyStr] = env.ToRyeValue(row.Values[1])
			}
			return *env.NewDict(data)
		},
	},

	"where-equal": {
		Argsn: 3,
		Doc:   "Returns table of rows where specific colum is equal to given value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch s := arg0.(type) {
			case *env.Table:
				spr = s
			case env.Table:
				spr = &s
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-equal")
			}
			switch col := arg1.(type) {
			case env.Word:
				return WhereEquals(ps, spr, ps.Idx.GetWord(col.Index), arg2)
			case env.String:
				return WhereEquals(ps, spr, col.Value, arg2)
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-equal")
			}
		},
	},

	"where-not-equal": {
		Argsn: 3,
		Doc:   "Returns table of rows where specific colum is not equal to given value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch s := arg0.(type) {
			case *env.Table:
				spr = s
			case env.Table:
				spr = &s
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-not-equal")
			}
			switch col := arg1.(type) {
			case env.Word:
				return WhereNotEquals(ps, spr, ps.Idx.GetWord(col.Index), arg2)
			case env.String:
				return WhereNotEquals(ps, spr, col.Value, arg2)
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-not-equal")
			}
		},
	},

	"where-void": {
		Argsn: 2,
		Doc:   "Returns table of rows where specific colum is void.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch s := arg0.(type) {
			case *env.Table:
				spr = s
			case env.Table:
				spr = &s
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-void")
			}
			switch col := arg1.(type) {
			case env.Word:
				return WhereEquals(ps, spr, ps.Idx.GetWord(col.Index), env.NewVoid())
			case env.String:
				return WhereEquals(ps, spr, col.Value, env.NewVoid())
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-void")
			}
		},
	},

	"where-match": {
		Argsn: 3,
		Doc:   "Returns table of rows where a specific colum matches a regex.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			var spr *env.Table
			switch sheet := arg0.(type) {
			case env.Table:
				spr = &sheet
			case *env.Table:
				spr = sheet
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-match")
			}
			switch reNative := arg2.(type) {
			case env.Native:
				re, ok := reNative.Value.(*regexp.Regexp)
				if !ok {
					return MakeArgError(ps, 2, []env.Type{env.NativeType}, "where-match")
				}
				switch col := arg1.(type) {
				case env.Word:
					return WhereMatch(ps, spr, ps.Idx.GetWord(col.Index), re)
				case env.String:
					return WhereMatch(ps, spr, col.Value, re)
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-match")
				}
			default:
				return MakeArgError(ps, 3, []env.Type{env.NativeType}, "where-match")
			}
		},
	},

	"where-contains": {
		Argsn: 3,
		Doc:   "Returns table of rows where specific colum contains a given string value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			var spr *env.Table
			switch sheet := arg0.(type) {
			case env.Table:
				spr = &sheet
			case *env.Table:
				spr = sheet
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-contains")
			}
			switch s := arg2.(type) {
			case env.String:
				switch col := arg1.(type) {
				case env.Word:
					return WhereContains(ps, spr, ps.Idx.GetWord(col.Index), s.Value, false)
				case env.String:
					return WhereContains(ps, spr, col.Value, s.Value, false)
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-contains")
				}
			default:
				return MakeArgError(ps, 3, []env.Type{env.StringType}, "where-contains")
			}
		},
	},

	"where-not-contains": {
		Argsn: 3,
		Doc:   "Returns table of rows where specific colum does not contain a given string value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			var spr *env.Table
			switch sheet := arg0.(type) {
			case env.Table:
				spr = &sheet
			case *env.Table:
				spr = sheet
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-not-contains")
			}
			switch s := arg2.(type) {
			case env.String:
				switch col := arg1.(type) {
				case env.Word:
					return WhereContains(ps, spr, ps.Idx.GetWord(col.Index), s.Value, true)
				case env.String:
					return WhereContains(ps, spr, col.Value, s.Value, true)
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-not-contains")
				}
			default:
				return MakeArgError(ps, 3, []env.Type{env.StringType}, "where-not-contains")
			}
		},
	},

	"where-greater": {
		Argsn: 3,
		Doc:   "Returns table of rows where specific colum is greater than given value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch sheet := arg0.(type) {
			case env.Table:
				spr = &sheet
			case *env.Table:
				spr = sheet
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-greater")
			}
			switch col := arg1.(type) {
			case env.Word:
				return WhereGreater(ps, spr, ps.Idx.GetWord(col.Index), arg2)
			case env.String:
				return WhereGreater(ps, spr, col.Value, arg2)
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-greater")
			}
		},
	},

	"where-lesser": {
		Argsn: 3,
		Doc:   "Returns table of rows where specific colum is lesser than given value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch sheet := arg0.(type) {
			case env.Table:
				spr = &sheet
			case *env.Table:
				spr = sheet
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-lesser")
			}
			switch col := arg1.(type) {
			case env.Word:
				return WhereLesser(ps, spr, ps.Idx.GetWord(col.Index), arg2)
			case env.String:
				return WhereLesser(ps, spr, col.Value, arg2)
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-lesser")
			}
		},
	},

	"where-between": {
		Argsn: 4,
		Doc:   "Returns table of rows where specific colum is between given values.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			var spr *env.Table
			switch sheet := arg0.(type) {
			case env.Table:
				spr = &sheet
			case *env.Table:
				spr = sheet
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-between")
			}
			switch col := arg1.(type) {
			case env.Word:
				return WhereBetween(ps, spr, ps.Idx.GetWord(col.Index), arg2, arg3, false)
			case env.String:
				return WhereBetween(ps, spr, col.Value, arg2, arg3, false)
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-between")
			}
		},
	},

	"where-between\\inclusive": {
		Argsn: 4,
		Doc:   "Returns table of rows where specific colum is between given values (inclusive).",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			var spr *env.Table
			switch sheet := arg0.(type) {
			case env.Table:
				spr = &sheet
			case *env.Table:
				spr = sheet
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-between\\inclusive")
			}
			switch col := arg1.(type) {
			case env.Word:
				return WhereBetween(ps, spr, ps.Idx.GetWord(col.Index), arg2, arg3, true)
			case env.String:
				return WhereBetween(ps, spr, col.Value, arg2, arg3, true)
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-between\\inclusive")
			}
		},
	},

	"where-in": {
		Argsn: 3,
		Doc:   "Returns table of rows where specific colum value is found in block of values.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			var spr *env.Table
			switch s := arg0.(type) {
			case env.Table:
				spr = &s
			case *env.Table:
				spr = s
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-in")
			}
			switch vals := arg2.(type) {
			case env.Block:
				switch col := arg1.(type) {
				case env.Word:
					return WhereIn(ps, spr, ps.Idx.GetWord(col.Index), vals.Series.S)
				case env.String:
					return WhereIn(ps, spr, col.Value, vals.Series.S)
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-in")
				}
			default:
				return MakeArgError(ps, 3, []env.Type{env.BlockType}, "where-in")
			}
		},
	},

	"where-not-in": {
		Argsn: 3,
		Doc:   "Returns table of rows where specific colum value is not found in block of values.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			var spr *env.Table
			switch s := arg0.(type) {
			case env.Table:
				spr = &s
			case *env.Table:
				spr = s
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where-not-in")
			}
			switch vals := arg2.(type) {
			case env.Block:
				switch col := arg1.(type) {
				case env.Word:
					return WhereNotIn(ps, spr, ps.Idx.GetWord(col.Index), vals.Series.S)
				case env.String:
					return WhereNotIn(ps, spr, col.Value, vals.Series.S)
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "where-not-in")
				}
			default:
				return MakeArgError(ps, 3, []env.Type{env.BlockType}, "where-not-in")
			}
		},
	},

	"where": {
		Argsn: 3,
		Doc:   "Returns table of rows where code block evaluates to true for the column value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch table := arg0.(type) {
			case env.Table:
				spr = &table
			case *env.Table:
				spr = table
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "where")
			}

			switch col := arg1.(type) {
			case env.Word:
				colName := ps.Idx.GetWord(col.Index)
				switch block := arg2.(type) {
				case env.Block:
					return WhereBlock(ps, spr, colName, block)
				case env.Builtin:
					return WhereBuiltin(ps, spr, colName, block)
				case env.Function:
					return WhereFunction(ps, spr, colName, block)
				default:
					return MakeArgError(ps, 3, []env.Type{env.BlockType, env.BuiltinType, env.FunctionType}, "where")
				}
			case env.String:
				colName := col.Value
				switch block := arg2.(type) {
				case env.Block:
					return WhereBlock(ps, spr, colName, block)
				case env.Builtin:
					return WhereBuiltin(ps, spr, colName, block)
				case env.Function:
					return WhereFunction(ps, spr, colName, block)
				default:
					return MakeArgError(ps, 3, []env.Type{env.BlockType, env.BuiltinType, env.FunctionType}, "where")
				}
			case env.Block:
				switch block := arg2.(type) {
				case env.Block:
					return WhereBlockMultiCol(ps, spr, col, block)
				default:
					return MakeArgError(ps, 3, []env.Type{env.BlockType}, "where")
				}
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType, env.BlockType}, "where")
			}
		},
	},

	"distinct": {
		Argsn: 2,
		Doc:   "Returns table with duplicate rows removed based on specified column(s).",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch s := arg0.(type) {
			case env.Table:
				spr = &s
			case *env.Table:
				spr = s
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "distinct")
			}
			switch col := arg1.(type) {
			case env.Word:
				return Distinct(ps, spr, []string{ps.Idx.GetWord(col.Index)})
			case env.String:
				return Distinct(ps, spr, []string{col.Value})
			case env.Block:
				var cols []string
				for _, word := range col.Series.S {
					switch w := word.(type) {
					case env.Word:
						cols = append(cols, ps.Idx.GetWord(w.Index))
					case env.Tagword:
						cols = append(cols, ps.Idx.GetWord(w.Index))
					case env.String:
						cols = append(cols, w.Value)
					default:
						return MakeBuiltinError(ps, "Column names block must contain only words or strings.", "distinct")
					}
				}
				return Distinct(ps, spr, cols)
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType, env.BlockType}, "distinct")
			}
		},
	},

	"fill-void": {
		Argsn: 3,
		Doc:   "Returns table with void values in specified column replaced with given value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch s := arg0.(type) {
			case env.Table:
				spr = &s
			case *env.Table:
				spr = s
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "fill-void")
			}
			var colName string
			switch col := arg1.(type) {
			case env.Word:
				colName = ps.Idx.GetWord(col.Index)
			case env.String:
				colName = col.Value
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "fill-void")
			}
			return FillVoid(ps, spr, colName, arg2)
		},
	},

	"count-where": {
		Argsn: 3,
		Doc:   "Returns count of rows where code block evaluates to true for the column value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var spr *env.Table
			switch table := arg0.(type) {
			case env.Table:
				spr = &table
			case *env.Table:
				spr = table
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "count-where")
			}

			switch col := arg1.(type) {
			case env.Word:
				colName := ps.Idx.GetWord(col.Index)
				switch block := arg2.(type) {
				case env.Block:
					return CountWhereBlock(ps, spr, colName, block)
				case env.Builtin:
					return CountWhereBuiltin(ps, spr, colName, block)
				case env.Function:
					return CountWhereFunction(ps, spr, colName, block)
				default:
					return MakeArgError(ps, 3, []env.Type{env.BlockType, env.BuiltinType, env.FunctionType}, "count-where")
				}
			case env.String:
				colName := col.Value
				switch block := arg2.(type) {
				case env.Block:
					return CountWhereBlock(ps, spr, colName, block)
				case env.Builtin:
					return CountWhereBuiltin(ps, spr, colName, block)
				case env.Function:
					return CountWhereFunction(ps, spr, colName, block)
				default:
					return MakeArgError(ps, 3, []env.Type{env.BlockType, env.BuiltinType, env.FunctionType}, "count-where")
				}
			case env.Block:
				switch block := arg2.(type) {
				case env.Block:
					return CountWhereBlockMultiCol(ps, spr, col, block)
				default:
					return MakeArgError(ps, 3, []env.Type{env.BlockType}, "count-where")
				}
			default:
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType, env.BlockType}, "count-where")
			}
		},
	},

	"add-row": {
		Argsn: 2,
		Doc:   "Returns a table with new-row added to it",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch table := arg0.(type) {
			case env.Table:
				switch bloc := arg1.(type) {
				case env.Block:
					vals := make([]any, bloc.Series.Len())
					for i := 0; i < bloc.Series.Len(); i++ {
						vals[i] = bloc.Series.Get(i)
					}
					table.AddRow(*env.NewTableRow(vals, &table))
					return table
				case env.List:
					vals := make([]any, len(bloc.Data))
					for i, v := range bloc.Data {
						vals[i] = env.ToRyeValue(v)
					}
					table.AddRow(*env.NewTableRow(vals, &table))
					return table
				default:
					return MakeArgError(ps, 2, []env.Type{env.BlockType, env.ListType}, "add-row")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "add-row")
			}
		},
	},

	"get-rows": {
		Argsn: 1,
		Doc:   "Get rows as a native. This value can be used in `add-rows` and `add-rows!`",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				rows := spr.GetRows()
				return *env.NewNative(ps.Idx, rows, "table-rows")
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "get-rows")
			}
		},
	},

	"add-rows": {
		Argsn: 2,
		Doc:   "Add one or more rows to a table",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				switch data1 := arg1.(type) {
				case env.Block:
					data := data1.Series
					for data.Pos() < data.Len() {
						rowd := make([]any, len(spr.Cols))
						for ii := 0; ii < len(spr.Cols); ii++ {
							k1 := data.Pop()
							rowd[ii] = k1
						}
						spr.AddRow(*env.NewTableRow(rowd, &spr))
					}
					return spr
				case env.List:
					data := data1.Data
					for item := range data {
						rowd := make([]any, len(spr.Cols))
						for ii := 0; ii < len(spr.Cols); ii++ {
							k1 := item
							rowd[ii] = k1
						}
						spr.AddRow(*env.NewTableRow(rowd, &spr))
					}
					return spr
				case env.Native:
					spr.Rows = append(spr.Rows, data1.Value.([]env.TableRow)...)
					return spr
				default:
					fmt.Println(data1.Inspect(*ps.Idx))
					return MakeArgError(ps, 2, []env.Type{env.BlockType, env.NativeType}, "add-rows")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "add-rows")
			}
		},
	},

	"add-rows!": {
		Argsn: 2,
		Doc:   "Add one or more rows to a table ref",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case *env.Table:
				switch data1 := arg1.(type) {
				case env.Block:
					data := data1.Series
					for data.Pos() < data.Len() {
						rowd := make([]any, len(spr.Cols))
						for ii := 0; ii < len(spr.Cols); ii++ {
							k1 := data.Pop()
							rowd[ii] = k1
						}
						spr.AddRow(*env.NewTableRow(rowd, spr))
					}
					return spr
				case env.Native:
					spr.Rows = append(spr.Rows, data1.Value.([]env.TableRow)...)
					return spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.BlockType, env.NativeType}, "add-rows!")
				}
			case *env.PersistentTable:
				switch data1 := arg1.(type) {
				case env.Block:
					data := data1.Series
					for data.Pos() < data.Len() {
						rowd := make([]any, len(spr.Cols))
						for ii := 0; ii < len(spr.Cols); ii++ {
							k1 := data.Pop()
							rowd[ii] = k1
						}
						spr.AddRow(*env.NewTableRow(rowd, spr))
					}
					return spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "add-rows!")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType, env.PersistentTableType}, "add-rows!")
			}
		},
	},

	"update-row!": {
		Argsn: 3,
		Doc:   `Update the row at the given index.`,
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case *env.Table:
				switch idx := arg1.(type) {
				case env.Integer:
					if idx.Value < 1 || (idx.Value-1) > int64(len(spr.Rows)) {
						errMsg := fmt.Sprintf("update-row! called with row index %d, but table only has %d rows", idx.Value, len(spr.Rows))
						return MakeError(ps, errMsg)
					}
					switch updater := arg2.(type) {
					case env.Function:
						CallFunction_CollectArgs(updater, ps, spr.Rows[idx.Value-1], false, ps.Ctx)
						ret := ps.Res
						if ok, err, row := RyeValueToTableRow(spr, ret); ok {
							spr.Rows[idx.Value-1] = *row
							return spr
						} else if len(err) > 0 {
							return MakeError(ps, err)
						} else {
							return MakeError(ps, fmt.Sprintf(
								"Function given to update-row! should have returned a Dict or a TableRow, but returned a %s %#v instead",
								NameOfRyeType(ret.Type()), ret,
							))
						}
					case env.Dict:

						row := spr.Rows[idx.Value-1]

						for keyStr, val := range updater.Data {
							index := spr.GetColumnIndex(keyStr)
							if index < 0 {
								return MakeError(ps, "Column "+keyStr+" was not found")
							}
							row.Values[index] = val
						}
						return spr
					case env.TableRow:
						spr.Rows[idx.Value-1] = updater
						return spr
					default:
						return MakeArgError(ps, 3, []env.Type{env.FunctionType, env.DictType, env.TableRowType}, "update-row!")
					}
				default:
					return MakeArgError(ps, 2, []env.Type{env.IntegerType}, "update-row!")
				}
			default:
				return MakeNeedsThawedArgError(ps, "update-row!")
			}

		},
	},

	"remove-row!": {
		Argsn: 2,
		Doc:   "Remove a row from a table by index",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case *env.Table:
				switch data1 := arg1.(type) {
				case env.Integer:
					if data1.Value > 0 && data1.Value <= int64(len(spr.Rows)) {
						spr.RemoveRowByIndex(data1.Value - 1)
						return spr
					} else {
						return MakeError(ps, fmt.Sprintf("Table had less then %d rows", data1.Value))
					}
				default:
					return MakeArgError(ps, 2, []env.Type{env.BlockType, env.NativeType}, "remove-row!")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "remove-row!")
			}
		},
	},

	"columns?": {
		Argsn: 2,
		Doc:   "Returns table with just given columns. Use lsetwords (:newName) to rename the previous column.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				switch col := arg1.(type) {
				case env.Block:
					cols := make([]string, 0)
					colNames := make([]string, 0)

					for _, obj := range col.Series.S {
						switch ww := obj.(type) {
						case env.String:
							cols = append(cols, ww.Value)
							colNames = append(colNames, ww.Value)
						case env.Tagword:
							colName := ps.Idx.GetWord(ww.Index)
							cols = append(cols, colName)
							colNames = append(colNames, colName)
						case env.LSetword:

							if len(colNames) == 0 {
								return MakeBuiltinError(ps, "LSetword :"+ps.Idx.GetWord(ww.Index)+" found but no previous column to rename", "columns?")
							}

							newName := ps.Idx.GetWord(ww.Index)
							colNames[len(colNames)-1] = newName
						default:
							return MakeBuiltinError(ps, "Expected string, tagword, or lsetword in columns specification", "columns?")
						}
					}
					return spr.ColumnsRenamed(ps, cols, colNames)
				default:
					return MakeArgError(ps, 1, []env.Type{env.BlockType}, "columns?")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "columns?")
			}
		},
	},

	"header?": {
		Argsn: 1,
		Doc:   "Gets the column names (header) as block.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case *env.Table:
				return spr.GetColumns()
			case env.Table:
				return spr.GetColumns()
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "headers?")
			}
		},
	},

	"column?": {
		Argsn: 2,
		Doc:   "Gets all values of a column as a block.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			switch spr := arg0.(type) {
			case env.Table:
				switch col := arg1.(type) {
				case env.Word:
					res := spr.GetColumn(ps.Idx.GetWord(col.Index))
					if _, isErr := res.(*env.Error); isErr {
						ps.FailureFlag = true
					}
					return res
				case env.String:
					res := spr.GetColumn(col.Value)
					if _, isErr := res.(*env.Error); isErr {
						ps.FailureFlag = true
					}
					return res
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "column?")
				}
			case env.Block:
				switch col := arg1.(type) {
				case env.Integer:
					col1 := make([]env.Object, len(spr.Series.S))
					if col.Value < 0 {
						return MakeBuiltinError(ps, "Index can't be negative", "column?")
					}
					for i, item_ := range spr.Series.S {
						switch item := item_.(type) {
						case env.Block:
							if len(item.Series.S) < int(col.Value) {
								return MakeBuiltinError(ps, "index out of bounds for item: "+strconv.Itoa(i), "column?")
							}
							col1[i] = item.Series.S[col.Value]
						}
					}
					return *env.NewBlock(*env.NewTSeries(col1))
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "column?")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "column?")
			}
		},
	},

	"drop-column": {
		Argsn: 2,
		Doc:   "Remove a column from a table. Returns new table",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				switch rmCol := arg1.(type) {
				case env.Word:
					return DropColumn(ps, spr, *env.NewString(ps.Idx.GetWord(rmCol.Index)))
				case env.String:
					return DropColumn(ps, spr, rmCol)
				case env.Block:
					return DropColumnBlock(ps, spr, rmCol)
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType, env.BlockType}, "drop-column")
				}
			}
			return MakeArgError(ps, 1, []env.Type{env.TableType}, "drop-column")
		},
	},

	"rename-column!": {
		Argsn: 3,
		Doc:   "Remove a column from a table. Returns new table",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case *env.Table:
				switch oldName := arg1.(type) {
				case env.String:
					switch newName := arg2.(type) {
					case env.String:
						return RenameColumn(ps, spr, oldName, newName)
					default:
						return MakeArgError(ps, 2, []env.Type{env.WordType, env.BlockType}, "rename-column")
					}
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.BlockType}, "rename-column")
				}
			}
			return MakeArgError(ps, 1, []env.Type{env.TableType}, "rename-column")
		},
	},

	"gen-column": {
		Argsn: 4,
		Doc:   "Adds a new column to table. Changes in-place and returns the new table.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				switch newCol := arg1.(type) {
				case env.Word:
					switch fromCols := arg2.(type) {
					case env.Block:
						switch code := arg3.(type) {
						case env.Block:
							return GenerateColumn(ps, spr, newCol, fromCols, code)
						default:
							return MakeArgError(ps, 4, []env.Type{env.BlockType}, "gen-column")
						}
					case env.Word:
						switch replaceBlock := arg3.(type) {
						case env.Block:
							if replaceBlock.Series.Len() != 2 {
								return MakeBuiltinError(ps, "Replacement block must contain a regex object and replacement string.", "genadd-umn")
							}
							regexNative, ok := replaceBlock.Series.S[0].(env.Native)
							if !ok {
								return MakeBuiltinError(ps, "First element of replacement block must be a regex object.", "genaddumn")
							}
							regex, ok := regexNative.Value.(*regexp.Regexp)
							if !ok {
								return MakeBuiltinError(ps, "First element of replacement block must be a regex object.", "gen-column")
							}
							replaceStr, ok := replaceBlock.Series.S[1].(env.String)
							if !ok {
								return MakeBuiltinError(ps, "Second element of replacement block must be a string.", "gen-column")
							}
							err := GenerateColumnRegexReplace(ps, &spr, newCol, fromCols, regex, replaceStr.Value)
							if err != nil {
								return err
							}
							return spr
						default:
							return MakeArgError(ps, 3, []env.Type{env.BlockType}, "gen-column")
						}
					default:
						return MakeArgError(ps, 3, []env.Type{env.BlockType}, "gen-column")
					}
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType}, "gen-column")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "gen-column")
			}
		},
	},

	"update-column!": {
		Argsn: 3,
		Doc:   "Updates all cells in a column by applying a function that receives the cell value and the row.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case *env.Table:
				var colName string
				switch col := arg1.(type) {
				case env.Word:
					colName = ps.Idx.GetWord(col.Index)
				case env.String:
					colName = col.Value
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "update-column!")
				}

				colIdx := slices.Index(spr.Cols, colName)
				if colIdx < 0 {
					return MakeBuiltinError(ps, fmt.Sprintf("Column '%s' not found", colName), "update-column!")
				}

				switch updater := arg2.(type) {
				case env.Function:
					for i := range spr.Rows {
						cell := env.ToRyeValue(spr.Rows[i].Values[colIdx])
						CallFunctionArgs2(updater, ps, cell, spr.Rows[i], ps.Ctx)
						if ps.ErrorFlag {
							return ps.Res
						}
						spr.Rows[i].Values[colIdx] = ps.Res
					}
					return spr
				default:
					return MakeArgError(ps, 3, []env.Type{env.FunctionType}, "update-column!")
				}
			default:
				return MakeNeedsThawedArgError(ps, "update-column!")
			}
		},
	},

	"add-column": {
		Argsn: 3,
		Doc:   "Adds a new column to table with the given data. Returns the modified table.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				var colName string
				switch col := arg1.(type) {
				case env.Word:
					colName = ps.Idx.GetWord(col.Index)
				case env.String:
					colName = col.Value
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType}, "add-column")
				}

				numRows := len(spr.Rows)

				switch data := arg2.(type) {
				case env.Block:

					if data.Series.Len() != numRows {
						return MakeBuiltinError(ps, fmt.Sprintf("Column data has %d values but table has %d rows", data.Series.Len(), numRows), "add-column")
					}

					spr.Cols = append(spr.Cols, colName)

					for i, row := range spr.Rows {
						row.Values = append(row.Values, data.Series.S[i])
						spr.Rows[i] = row
					}
					return spr
				case env.List:
					if len(data.Data) != numRows {
						return MakeBuiltinError(ps, fmt.Sprintf("Column data has %d values but table has %d rows", len(data.Data), numRows), "add-column")
					}

					spr.Cols = append(spr.Cols, colName)

					for i, row := range spr.Rows {
						row.Values = append(row.Values, env.ToRyeValue(data.Data[i]))
						spr.Rows[i] = row
					}
					return spr
				default:
					return MakeArgError(ps, 3, []env.Type{env.BlockType, env.ListType}, "add-column")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "add-column")
			}
		},
	},

	"order-by!": {
		Argsn: 3,
		Doc:   "Sorts row by given column, changes table in place.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			dir, ok := arg2.(env.Word)
			if !ok {
				return MakeArgError(ps, 3, []env.Type{env.WordType}, "sort-by!")
			}
			var dirAsc bool
			if dir.Index == ps.Idx.IndexWord("asc") {
				dirAsc = true
			} else if dir.Index == ps.Idx.IndexWord("desc") {
				dirAsc = false
			} else {
				return MakeBuiltinError(ps, "Direction can be just asc or desc.", "sort-by!")
			}
			switch spr := arg0.(type) {
			case env.Table:
				switch col := arg1.(type) {
				case env.String:
					if dirAsc {
						SortByColumn(ps, &spr, col.Value)
					} else {
						SortByColumnDesc(ps, &spr, col.Value)
					}
					return spr
				case env.Word:
					if dirAsc {
						SortByColumn(ps, &spr, ps.Idx.GetWord(col.Index))
					} else {
						SortByColumnDesc(ps, &spr, ps.Idx.GetWord(col.Index))
					}
					return spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType}, "sort-by!")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "sort-by!")
			}
		},
	},

	"order-by": {
		Argsn: 3,
		Doc:   "Sorts row by given column, changes table in place.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			dir, ok := arg2.(env.Word)
			if !ok {
				return MakeArgError(ps, 3, []env.Type{env.WordType}, "sort-by!")
			}
			var dirAsc bool
			if dir.Index == ps.Idx.IndexWord("asc") {
				dirAsc = true
			} else if dir.Index == ps.Idx.IndexWord("desc") {
				dirAsc = false
			} else {
				return MakeBuiltinError(ps, "Direction can be just asc or desc.", "sort-by!")
			}
			switch spr := arg0.(type) {
			case env.Table:
				copied := make([]env.TableRow, len(spr.Rows))
				copy(copied, spr.Rows)
				newSpr := env.NewTable(spr.Cols)
				newSpr.Rows = copied
				switch col := arg1.(type) {
				case env.String:
					if dirAsc {
						SortByColumn(ps, newSpr, col.Value)
					} else {
						SortByColumnDesc(ps, newSpr, col.Value)
					}
					return *newSpr
				case env.Word:
					if dirAsc {
						SortByColumn(ps, newSpr, ps.Idx.GetWord(col.Index))
					} else {
						SortByColumnDesc(ps, newSpr, ps.Idx.GetWord(col.Index))
					}
					return *newSpr
				default:
					return MakeArgError(ps, 2, []env.Type{env.WordType}, "sort-by!")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "sort-by!")
			}
		},
	},

	"add-indexes!": {
		Argsn: 2,
		Doc:   "Creates an index for all values in the provided columns. Changes in-place and returns the new table.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				switch col := arg1.(type) {
				case env.Block:
					colWords := make([]env.Word, col.Series.Len())
					for c := range col.Series.S {
						switch ww := col.Series.S[c].(type) {
						case env.Word:
							colWords[c] = ww
						default:
							return MakeError(ps, "Block of tagwords needed")
						}
					}
					err := AddIndexes(ps, &spr, colWords)
					if err != nil {
						return err
					}
					return spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "add-indexes!")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "add-indexes!")
			}
		},
	},

	"indexes?": {
		Argsn: 1,
		Doc:   "Returns the columns that are indexed in a table.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			switch spr := arg0.(type) {
			case env.Table:
				res := make([]env.Object, 0)
				for col := range spr.Indexes {
					res = append(res, *env.NewString(col))
				}
				return *env.NewBlock(*env.NewTSeries(res))
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "indexes?")
			}
		},
	},

	"autotype": {
		Argsn: 2,
		Doc:   "Takes a table and tries to determine and change the types of columns.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			switch spr := arg0.(type) {
			case env.Table:
				switch percent := arg1.(type) {
				case env.Decimal:
					return AutoType(ps, &spr, percent.Value)
				default:
					return MakeArgError(ps, 2, []env.Type{env.DecimalType}, "autotype")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "autotype")
			}
		},
	},

	"left-join": {
		Argsn: 4,
		Doc:   "Left joins two tables on the given columns.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			switch spr1 := arg0.(type) {
			case env.Table:
				switch spr2 := arg1.(type) {
				case env.Table:
					switch col1 := arg2.(type) {
					case env.Word:
						col2, ok := arg3.(env.Word)
						if !ok {
							return MakeArgError(ps, 4, []env.Type{env.WordType}, "left-join")
						}
						return LeftJoin(ps, spr1, spr2, ps.Idx.GetWord(col1.Index), ps.Idx.GetWord(col2.Index), false)
					case env.String:
						col2, ok := arg3.(env.String)
						if !ok {
							MakeArgError(ps, 4, []env.Type{env.StringType}, "left-join")
						}
						return LeftJoin(ps, spr1, spr2, col1.Value, col2.Value, false)
					default:
						return MakeArgError(ps, 3, []env.Type{env.WordType, env.StringType}, "left-join")
					}
				default:
					return MakeArgError(ps, 2, []env.Type{env.TableType}, "left-join")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "left-join")
			}
		},
	},

	"inner-join": {
		Argsn: 4,
		Doc:   "Inner joins two tables on the given columns.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			switch spr1 := arg0.(type) {
			case env.Table:
				switch spr2 := arg1.(type) {
				case env.Table:
					switch col1 := arg2.(type) {
					case env.Word:
						col2, ok := arg3.(env.Word)
						if !ok {
							return MakeArgError(ps, 4, []env.Type{env.WordType}, "inner-join")
						}
						return LeftJoin(ps, spr1, spr2, ps.Idx.GetWord(col1.Index), ps.Idx.GetWord(col2.Index), true)
					case env.String:
						col2, ok := arg3.(env.String)
						if !ok {
							MakeArgError(ps, 4, []env.Type{env.StringType}, "inner-join")
						}
						return LeftJoin(ps, spr1, spr2, col1.Value, col2.Value, true)
					default:
						return MakeArgError(ps, 3, []env.Type{env.WordType, env.StringType}, "inner-join")
					}
				default:
					return MakeArgError(ps, 2, []env.Type{env.TableType}, "inner-join")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "inner-join")
			}
		},
	},

	"group-by": {
		Argsn: 3,
		Doc:   "Groups a table by the given column(s) and (optional) aggregations.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) (res env.Object) {
			switch spr := arg0.(type) {
			case env.Table:
				switch aggBlock := arg2.(type) {
				case env.Block:
					if len(aggBlock.Series.S)%2 != 0 {
						return MakeBuiltinError(ps, "Aggregation block must contain pairs of column name and function for each aggregation.", "group-by")
					}
					aggregations := make(map[string][]string)
					for i := 0; i < len(aggBlock.Series.S); i += 2 {
						col := aggBlock.Series.S[i]
						fun, ok := aggBlock.Series.S[i+1].(env.Word)
						if !ok {
							return MakeBuiltinError(ps, "Aggregation function must be a word", "group-by")
						}
						colStr := ""
						switch col := col.(type) {
						case env.Tagword:
							colStr = ps.Idx.GetWord(col.Index)
						case env.String:
							colStr = col.Value
						default:
							return MakeBuiltinError(ps, "Aggregation column must be a word or string", "group-by")
						}
						funStr := ps.Idx.GetWord(fun.Index)
						aggregations[colStr] = append(aggregations[colStr], funStr)
					}
					switch col := arg1.(type) {
					case env.Word:
						return GroupBy(ps, spr, []string{ps.Idx.GetWord(col.Index)}, aggregations)
					case env.String:
						return GroupBy(ps, spr, []string{col.Value}, aggregations)
					case env.Block:
						cols := make([]string, col.Series.Len())
						for c := range col.Series.S {
							switch ww := col.Series.S[c].(type) {
							case env.String:
								cols[c] = ww.Value
							case env.Tagword:
								cols[c] = ps.Idx.GetWord(ww.Index)
							case env.Word:
								cols[c] = ps.Idx.GetWord(ww.Index)
							default:
								return MakeBuiltinError(ps, "Block must contain only strings or words for column names", "group-by")
							}
						}
						return GroupBy(ps, spr, cols, aggregations)
					default:
						return MakeArgError(ps, 2, []env.Type{env.WordType, env.StringType, env.BlockType}, "group-by")
					}
				default:
					return MakeArgError(ps, 3, []env.Type{env.BlockType}, "group-by")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "group-by")
			}
		},
	},

	"A1": {
		Argsn: 1,
		Doc:   "Accepts a Table and returns the first row first column cell.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch s0 := arg0.(type) {
			case env.Table:
				r := s0.Rows[0].Values[0]
				return env.ToRyeValue(r)

			default:
				ps.ErrorFlag = true
				return env.NewError("first arg not table")
			}
		},
	},

	"B1": {
		Argsn: 1,
		Doc:   "Accepts a Table and returns the first row second column cell.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch s0 := arg0.(type) {
			case env.Table:
				r := s0.Rows[0].Values[1]
				return env.ToRyeValue(r)

			default:
				ps.ErrorFlag = true
				return env.NewError("first arg not table")
			}
		},
	},

	"A2": {
		Argsn: 1,
		Doc:   "Accepts a Table and returns the second row first column cell.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch s0 := arg0.(type) {
			case env.Table:
				r := s0.Rows[1].Values[0]
				return env.ToRyeValue(r)

			default:
				ps.ErrorFlag = true
				return env.NewError("first arg not table")
			}
		},
	},

	"B2": {
		Argsn: 1,
		Doc:   "Accepts a Table and returns the second row second column cell.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch s0 := arg0.(type) {
			case env.Table:
				r := s0.Rows[1].Values[1]
				return env.ToRyeValue(r)

			default:
				ps.ErrorFlag = true
				return env.NewError("first arg not table")
			}
		},
	},

	"file-uri//Load\\csv": {

		Argsn: 1,
		Doc:   "Loads a .csv file to a table datatype.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch file := arg0.(type) {
			case env.Uri:

				f, err := os.Open(file.GetPath())
				if err != nil {

					return MakeBuiltinError(ps, "Unable to read input file:"+err.Error(), "Load\\csv")
				}
				defer f.Close()

				csvReader := csv.NewReader(f)
				rows, err := csvReader.ReadAll()
				if err != nil {

					return MakeBuiltinError(ps, "Unable to parse file as CSV: "+err.Error(), "Load\\csv")
				}
				if len(rows) == 0 {
					return MakeBuiltinError(ps, "File is empty", "Load\\csv")
				}
				spr := env.NewTable(rows[0])

				if len(rows) > 1 {
					for _, row := range rows[1:] {
						anyRow := make([]any, len(row))
						for i, v := range row {
							anyRow[i] = *env.NewString(v)
						}
						spr.AddRow(*env.NewTableRow(anyRow, spr))
					}
				}
				return *spr
			default:
				return MakeArgError(ps, 1, []env.Type{env.UriType}, "Load\\csv")
			}
		},
	},

	"file-uri//Load\\csv\\": {

		Argsn: 2,
		Doc:   "Loads a .csv file to a table datatype.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch file := arg0.(type) {
			case env.Uri:
				switch separator := arg1.(type) {
				case env.String:

					f, err := os.Open(file.GetPath())
					if err != nil {

						return MakeBuiltinError(ps, "Unable to read input file:"+err.Error(), "Load\\csv")
					}
					defer f.Close()

					csvReader := csv.NewReader(f)
					if len(separator.Value) != 1 {
						return MakeBuiltinError(ps, "Separator must be exactly 1 character long", "Load\\csv\\")
					}
					csvReader.Comma = rune(separator.Value[0])
					rows, err := csvReader.ReadAll()
					if err != nil {

						return MakeBuiltinError(ps, "Unable to parse file as CSV: "+err.Error(), "Load\\csv")
					}
					if len(rows) == 0 {
						return MakeBuiltinError(ps, "File is empty", "Load\\csv")
					}
					spr := env.NewTable(rows[0])

					if len(rows) > 1 {
						for _, row := range rows[1:] {
							anyRow := make([]any, len(row))
							for i, v := range row {
								anyRow[i] = *env.NewString(v)
							}
							spr.AddRow(*env.NewTableRow(anyRow, spr))
						}
					}
					return *spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.StringType}, "Load\\csv\\")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.UriType}, "Load\\csv\\")
			}
		},
	},

	"file-uri//Load\\tsv": {

		Argsn: 1,
		Doc:   "Loads a .csv file to a table datatype.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch file := arg0.(type) {
			case env.Uri:

				f, err := os.Open(file.GetPath())
				if err != nil {

					return MakeBuiltinError(ps, "Unable to read input file:"+err.Error(), "Load\\csv")
				}
				defer f.Close()

				csvReader := csv.NewReader(f)
				csvReader.Comma = '\t'
				rows, err := csvReader.ReadAll()
				if err != nil {

					return MakeBuiltinError(ps, "Unable to parse file as CSV: "+err.Error(), "Load\\csv")
				}
				if len(rows) == 0 {
					return MakeBuiltinError(ps, "File is empty", "Load\\csv")
				}
				spr := env.NewTable(rows[0])

				if len(rows) > 1 {
					for _, row := range rows[1:] {
						anyRow := make([]any, len(row))
						for i, v := range row {
							anyRow[i] = *env.NewString(v)
						}
						spr.AddRow(*env.NewTableRow(anyRow, spr))
					}
				}
				return *spr
			default:
				return MakeArgError(ps, 1, []env.Type{env.UriType}, "Load\\csv")
			}
		},
	},

	"file-uri//Save\\csv": {
		Argsn: 2,
		Doc:   "Saves a table to a .csv file.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch file := arg0.(type) {
			case env.Uri:
				switch spr := arg1.(type) {
				case env.Table:

					f, err := os.Create(file.GetPath())
					if err != nil {

						return MakeBuiltinError(ps, "Unable to create input file.", "file-uri//Save\\csv")
					}
					defer f.Close()

					cLen := len(spr.Cols)

					csvWriter := csv.NewWriter(f)

					err1 := csvWriter.Write(spr.Cols)
					if err1 != nil {
						return MakeBuiltinError(ps, "Unable to create write header.", "file-uri//Save\\csv")
					}

					for ir, row := range spr.Rows {
						strVals := make([]string, cLen)

						for i, v := range row.Values {
							var sv string
							switch tv := v.(type) {
							case string:
								sv = tv
							case int64:
								sv = strconv.Itoa(int(tv))
							case float64:
								sv = strconv.FormatFloat(tv, 'f', -1, 64)
							case env.String:
								sv = tv.Value
							case env.Integer:
								sv = strconv.Itoa(int(tv.Value))
							case env.Decimal:
								sv = fmt.Sprintf("%f", tv.Value)
							}
							if i < cLen {
								strVals[i] = sv
							}
						}
						err := csvWriter.Write(strVals)
						if err != nil {
							return MakeBuiltinError(ps, "Unable to write line: "+strconv.Itoa(ir), "file-uri//Save\\csv")
						}
					}
					csvWriter.Flush()
					f.Close()
					return spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.TableType}, "file-uri//Save\\csv")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.UriType}, "file-uri//Save\\csv")
			}
		},
	},

	"file-uri//Save\\tsv": {
		Argsn: 2,
		Doc:   "Saves a table to a .tsv file.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch file := arg0.(type) {
			case env.Uri:
				switch spr := arg1.(type) {
				case env.Table:

					f, err := os.Create(file.GetPath())
					if err != nil {

						return MakeBuiltinError(ps, "Unable to create input file.", "file-uri//Save\\tsv")
					}
					defer f.Close()

					cLen := len(spr.Cols)

					csvWriter := csv.NewWriter(f)
					csvWriter.Comma = '\t'
					err1 := csvWriter.Write(spr.Cols)
					if err1 != nil {
						return MakeBuiltinError(ps, "Unable to create write header.", "file-uri//Save\\tsv")
					}

					for ir, row := range spr.Rows {
						strVals := make([]string, cLen)

						for i, v := range row.Values {
							var sv string
							switch tv := v.(type) {
							case string:
								sv = tv
							case int64:
								sv = strconv.Itoa(int(tv))
							case float64:
								sv = strconv.FormatFloat(tv, 'f', -1, 64)
							case env.String:
								sv = tv.Value
							case env.Integer:
								sv = strconv.Itoa(int(tv.Value))
							case env.Decimal:
								sv = fmt.Sprintf("%f", tv.Value)
							}
							if i < cLen {
								strVals[i] = sv
							}
						}
						err := csvWriter.Write(strVals)
						if err != nil {
							return MakeBuiltinError(ps, "Unable to write line: "+strconv.Itoa(ir), "file-uri//Save\\tsv")
						}
					}
					csvWriter.Flush()
					f.Close()
					return spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.TableType}, "file-uri//Save\\tsv")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.UriType}, "file-uri//Save\\tsv")
			}
		},
	},

	"file-uri//Load\\xlsx": {
		Argsn: 1,
		Doc:   "Loads the first sheet in an .xlsx file to a Table.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch file := arg0.(type) {
			case env.Uri:
				f, err := excelize.OpenFile(file.GetPath())
				if err != nil {
					return MakeBuiltinError(ps, fmt.Sprintf("Unable to open file: %s", err), "Load\\xlsx")
				}
				defer f.Close()

				sheetMap := f.GetSheetMap()
				if len(sheetMap) == 0 {
					return MakeBuiltinError(ps, "No sheets found in file", "Load\\xlsx")
				}

				sheetName := sheetMap[1]
				rows, err := f.Rows(sheetName)
				if err != nil {
					return MakeBuiltinError(ps, fmt.Sprintf("Unable to get rows from sheet: %s", err), "Load\\xlsx")
				}
				rows.Next()
				header, err := rows.Columns()
				if err != nil {
					return MakeBuiltinError(ps, fmt.Sprintf("Unable to get columns from sheet: %s", err), "Load\\xlsx")
				}
				if len(header) == 0 {
					return MakeBuiltinError(ps, "Header row is empty", "Load\\xlsx")
				}
				spr := env.NewTable(header)
				for rows.Next() {
					row, err := rows.Columns()
					if err != nil {
						return MakeBuiltinError(ps, fmt.Sprintf("Unable to get row: %s", err), "Load\\xlsx")
					}
					anyRow := make([]any, len(row))
					for i, v := range row {
						anyRow[i] = *env.NewString(v)
					}

					for i := len(row); i < len(spr.Cols); i++ {
						anyRow[i] = *env.NewString("")
					}
					spr.AddRow(*env.NewTableRow(anyRow, spr))
				}
				return *spr
			default:
				return MakeArgError(ps, 1, []env.Type{env.UriType}, "Load\\xlsx")
			}
		},
	},

	"file-uri//Save\\xlsx": {
		Argsn: 2,
		Doc:   "Saves a Table to a .xlsx file.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch file := arg0.(type) {
			case env.Uri:
				switch spr := arg1.(type) {
				case env.Table:
					sheetName := "Sheet1"
					f := excelize.NewFile()
					index, err := f.NewSheet(sheetName)
					if err != nil {
						return MakeBuiltinError(ps, fmt.Sprintf("Unable to create new sheet: %s", err), "file-uri//Save\\xlsx")
					}
					err = f.SetSheetRow(sheetName, "A1", &spr.Cols)
					if err != nil {
						return MakeBuiltinError(ps, fmt.Sprintf("Unable to set header row: %s", err), "file-uri//Save\\xlsx")
					}
					for i, row := range spr.Rows {

						rowIndex := i + 2
						vals := make([]any, len(row.Values))
						for j, v := range row.Values {
							switch val := v.(type) {
							case env.String:
								vals[j] = val.Value
							case string:
								vals[j] = val
							case env.Integer:
								vals[j] = val.Value
							case int64:
								vals[j] = val
							case env.Decimal:
								vals[j] = val.Value
							case float64:
								vals[j] = val
							default:
								return MakeBuiltinError(ps, fmt.Sprintf("Unable to save table: unsupported type %T", val), "file-uri//Save\\xlsx")
							}
						}
						err = f.SetSheetRow(sheetName, fmt.Sprintf("A%d", rowIndex), &vals)
						if err != nil {
							return MakeBuiltinError(ps, fmt.Sprintf("Unable to set row %d: %s", rowIndex, err), "file-uri//Save\\xlsx")
						}
					}
					f.SetActiveSheet(index)
					err = f.SaveAs(file.GetPath())
					if err != nil {
						return MakeBuiltinError(ps, fmt.Sprintf("Unable to save table: %s", err), "file-uri//Save\\xlsx")
					}
					return spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.TableType}, "file-uri//Save\\xlsx")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.UriType}, "file-uri//Save\\xlsx")
			}
		},
	},

	"persistent-table": {
		Argsn: 3,
		Doc:   "Creates a persistent table using BadgerDB for storage",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			cols, err := ColNames(ps, arg0, "persistent-table")
			if err != nil {
				return err
			}

			switch dbPath := arg1.(type) {
			case env.String:
				switch tableName := arg2.(type) {
				case env.String:
					pt, err := env.NewPersistentTable(cols, dbPath.Value, tableName.Value)
					if err != nil {
						return MakeBuiltinError(ps, fmt.Sprintf("Failed to create persistent table: %v", err), "persistent-table")
					}
					return pt
				default:
					return MakeArgError(ps, 3, []env.Type{env.StringType}, "persistent-table")
				}
			default:
				return MakeArgError(ps, 2, []env.Type{env.StringType}, "persistent-table")
			}
		},
	},

	"close-persistent-table!": {
		Argsn: 1,
		Doc:   "Closes the BadgerDB connection for a persistent table",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch pt := arg0.(type) {
			case *env.PersistentTable:
				err := pt.Close()
				if err != nil {
					return MakeBuiltinError(ps, fmt.Sprintf("Failed to close persistent table: %v", err), "close-persistent-table!")
				}
				return pt
			default:
				return MakeArgError(ps, 1, []env.Type{env.PersistentTableType}, "close-persistent-table!")
			}
		},
	},

	"parse\\csv": {
		Argsn: 1,
		Doc:   "Parses a CSV string to a table datatype.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch csvData := arg0.(type) {
			case env.String:
				csvReader := csv.NewReader(strings.NewReader(csvData.Value))
				rows, err := csvReader.ReadAll()
				if err != nil {
					return MakeBuiltinError(ps, "Unable to parse string as CSV: "+err.Error(), "parse\\csv")
				}
				if len(rows) == 0 {
					return MakeBuiltinError(ps, "CSV data is empty", "parse\\csv")
				}
				spr := env.NewTable(rows[0])
				if len(rows) > 1 {
					for _, row := range rows[1:] {
						anyRow := make([]any, len(row))
						for i, v := range row {
							anyRow[i] = *env.NewString(v)
						}
						spr.AddRow(*env.NewTableRow(anyRow, spr))
					}
				}
				return *spr
			default:
				return MakeArgError(ps, 1, []env.Type{env.StringType}, "parse\\csv")
			}
		},
	},

	"parse\\csv\\separator": {
		Argsn: 2,
		Doc:   "Parses a CSV string to a table datatype with a custom separator character.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch csvData := arg0.(type) {
			case env.String:
				switch separator := arg1.(type) {
				case env.String:
					if len(separator.Value) != 1 {
						return MakeBuiltinError(ps, "Separator must be exactly 1 character long", "parse\\csv\\separator")
					}
					csvReader := csv.NewReader(strings.NewReader(csvData.Value))
					csvReader.Comma = rune(separator.Value[0])
					rows, err := csvReader.ReadAll()
					if err != nil {
						return MakeBuiltinError(ps, "Unable to parse string as CSV: "+err.Error(), "parse\\csv\\separator")
					}
					if len(rows) == 0 {
						return MakeBuiltinError(ps, "CSV data is empty", "parse\\csv\\separator")
					}
					spr := env.NewTable(rows[0])
					if len(rows) > 1 {
						for _, row := range rows[1:] {
							anyRow := make([]any, len(row))
							for i, v := range row {
								anyRow[i] = *env.NewString(v)
							}
							spr.AddRow(*env.NewTableRow(anyRow, spr))
						}
					}
					return *spr
				default:
					return MakeArgError(ps, 2, []env.Type{env.StringType}, "parse\\csv\\separator")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.StringType}, "parse\\csv\\separator")
			}
		},
	},

	"parse\\tsv": {
		Argsn: 1,
		Doc:   "Parses a TSV (tab-separated values) string to a table datatype.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch tsvData := arg0.(type) {
			case env.String:
				csvReader := csv.NewReader(strings.NewReader(tsvData.Value))
				csvReader.Comma = '\t'
				rows, err := csvReader.ReadAll()
				if err != nil {
					return MakeBuiltinError(ps, "Unable to parse string as TSV: "+err.Error(), "parse\\tsv")
				}
				if len(rows) == 0 {
					return MakeBuiltinError(ps, "TSV data is empty", "parse\\tsv")
				}
				spr := env.NewTable(rows[0])
				if len(rows) > 1 {
					for _, row := range rows[1:] {
						anyRow := make([]any, len(row))
						for i, v := range row {
							anyRow[i] = *env.NewString(v)
						}
						spr.AddRow(*env.NewTableRow(anyRow, spr))
					}
				}
				return *spr
			default:
				return MakeArgError(ps, 1, []env.Type{env.StringType}, "parse\\tsv")
			}
		},
	},

	"format\\csv": {
		Argsn: 1,
		Doc:   "Converts a table to a CSV string.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				var buf strings.Builder
				csvWriter := csv.NewWriter(&buf)

				cLen := len(spr.Cols)

				err1 := csvWriter.Write(spr.Cols)
				if err1 != nil {
					return MakeBuiltinError(ps, "Unable to write header.", "format\\csv")
				}

				for ir, row := range spr.Rows {
					strVals := make([]string, cLen)

					for i, v := range row.Values {
						var sv string
						switch tv := v.(type) {
						case string:
							sv = tv
						case int64:
							sv = strconv.Itoa(int(tv))
						case float64:
							sv = strconv.FormatFloat(tv, 'f', -1, 64)
						case env.String:
							sv = tv.Value
						case env.Integer:
							sv = strconv.Itoa(int(tv.Value))
						case env.Decimal:
							sv = fmt.Sprintf("%f", tv.Value)
						}
						if i < cLen {
							strVals[i] = sv
						}
					}
					err := csvWriter.Write(strVals)
					if err != nil {
						return MakeBuiltinError(ps, "Unable to write line: "+strconv.Itoa(ir), "format\\csv")
					}
				}
				csvWriter.Flush()
				return *env.NewString(buf.String())
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "format\\csv")
			}
		},
	},

	"format\\tsv": {
		Argsn: 1,
		Doc:   "Converts a table to a TSV string.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				var buf strings.Builder
				csvWriter := csv.NewWriter(&buf)
				csvWriter.Comma = '\t'

				cLen := len(spr.Cols)

				err1 := csvWriter.Write(spr.Cols)
				if err1 != nil {
					return MakeBuiltinError(ps, "Unable to write header.", "format\\tsv")
				}

				for ir, row := range spr.Rows {
					strVals := make([]string, cLen)

					for i, v := range row.Values {
						var sv string
						switch tv := v.(type) {
						case string:
							sv = tv
						case int64:
							sv = strconv.Itoa(int(tv))
						case float64:
							sv = strconv.FormatFloat(tv, 'f', -1, 64)
						case env.String:
							sv = tv.Value
						case env.Integer:
							sv = strconv.Itoa(int(tv.Value))
						case env.Decimal:
							sv = fmt.Sprintf("%f", tv.Value)
						}
						if i < cLen {
							strVals[i] = sv
						}
					}
					err := csvWriter.Write(strVals)
					if err != nil {
						return MakeBuiltinError(ps, "Unable to write line: "+strconv.Itoa(ir), "format\\tsv")
					}
				}
				csvWriter.Flush()
				return *env.NewString(buf.String())
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "format\\tsv")
			}
		},
	},

	"parse\\xlsx": {
		Argsn: 1,
		Doc:   "Parses an xlsx binary string or bytes into a table (first sheet).",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var data []byte

			switch input := arg0.(type) {
			case env.String:
				data = []byte(input.Value)
			case env.Bytes:
				data = input.Value
			default:
				return MakeArgError(ps, 1, []env.Type{env.StringType, env.BytesType}, "parse\\xlsx")
			}

			tmpFile, err := os.CreateTemp("", "parse_xlsx_*.xlsx")
			if err != nil {
				return MakeBuiltinError(ps, "Unable to create temporary file: "+err.Error(), "parse\\xlsx")
			}
			defer os.Remove(tmpFile.Name())
			defer tmpFile.Close()

			_, err = tmpFile.Write(data)
			if err != nil {
				return MakeBuiltinError(ps, "Unable to write to temporary file: "+err.Error(), "parse\\xlsx")
			}
			tmpFile.Close()

			f, err := excelize.OpenFile(tmpFile.Name())
			if err != nil {
				return MakeBuiltinError(ps, "Unable to open xlsx data: "+err.Error(), "parse\\xlsx")
			}
			defer f.Close()

			sheetMap := f.GetSheetMap()
			if len(sheetMap) == 0 {
				return MakeBuiltinError(ps, "No sheets found in xlsx data", "parse\\xlsx")
			}

			sheetName := sheetMap[1]
			rows, err := f.Rows(sheetName)
			if err != nil {
				return MakeBuiltinError(ps, "Unable to get rows from sheet: "+err.Error(), "parse\\xlsx")
			}
			rows.Next()
			header, err := rows.Columns()
			if err != nil {
				return MakeBuiltinError(ps, "Unable to get columns from sheet: "+err.Error(), "parse\\xlsx")
			}
			if len(header) == 0 {
				return MakeBuiltinError(ps, "Header row is empty", "parse\\xlsx")
			}
			spr := env.NewTable(header)
			for rows.Next() {
				row, err := rows.Columns()
				if err != nil {
					return MakeBuiltinError(ps, "Unable to get row: "+err.Error(), "parse\\xlsx")
				}
				anyRow := make([]any, len(header))
				for i, v := range row {
					if i < len(header) {
						anyRow[i] = *env.NewString(v)
					}
				}

				for i := len(row); i < len(header); i++ {
					anyRow[i] = *env.NewString("")
				}
				spr.AddRow(*env.NewTableRow(anyRow, spr))
			}
			return *spr
		},
	},

	"format\\xlsx": {
		Argsn: 1,
		Doc:   "Converts a table to an XLSX as bytes native value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch spr := arg0.(type) {
			case env.Table:
				sheetName := "Sheet1"
				f := excelize.NewFile()
				index, err := f.NewSheet(sheetName)
				if err != nil {
					return MakeBuiltinError(ps, "Unable to create new sheet: "+err.Error(), "format\\xlsx")
				}
				err = f.SetSheetRow(sheetName, "A1", &spr.Cols)
				if err != nil {
					return MakeBuiltinError(ps, "Unable to set header row: "+err.Error(), "format\\xlsx")
				}
				for i, row := range spr.Rows {

					rowIndex := i + 2
					vals := make([]any, len(row.Values))
					for j, v := range row.Values {
						switch val := v.(type) {
						case env.String:
							vals[j] = val.Value
						case string:
							vals[j] = val
						case env.Integer:
							vals[j] = val.Value
						case int64:
							vals[j] = val
						case env.Decimal:
							vals[j] = val.Value
						case float64:
							vals[j] = val
						default:
							return MakeBuiltinError(ps, "Unable to format table: unsupported type "+fmt.Sprintf("%T", val), "format\\xlsx")
						}
					}
					err = f.SetSheetRow(sheetName, fmt.Sprintf("A%d", rowIndex), &vals)
					if err != nil {
						return MakeBuiltinError(ps, "Unable to set row "+strconv.Itoa(rowIndex)+": "+err.Error(), "format\\xlsx")
					}
				}
				f.SetActiveSheet(index)

				tmpFile, err := os.CreateTemp("", "format_xlsx_*.xlsx")
				if err != nil {
					return MakeBuiltinError(ps, "Unable to create temporary file: "+err.Error(), "format\\xlsx")
				}
				defer os.Remove(tmpFile.Name())
				defer tmpFile.Close()

				err = f.SaveAs(tmpFile.Name())
				if err != nil {
					return MakeBuiltinError(ps, "Unable to save xlsx data: "+err.Error(), "format\\xlsx")
				}

				data, err := os.ReadFile(tmpFile.Name())
				if err != nil {
					return MakeBuiltinError(ps, "Unable to read xlsx data: "+err.Error(), "format\\xlsx")
				}

				return *env.NewBytes(data)
			default:
				return MakeArgError(ps, 1, []env.Type{env.TableType}, "format\\xlsx")
			}
		},
	},
}
View Source
var Builtins_validation = map[string]*env.Builtin{

	"validate": {
		Argsn: 2,
		Doc:   "Validates and transforms data according to specified rules, returning a dictionary with converted values or an error.",
		Fn: func(env1 *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			return BuiValidate(env1, arg0, arg1)
		},
	},

	"validate>ctx": {
		Argsn: 2,
		Doc:   "Validates and transforms data according to specified rules, returning a context object for easy field access.",
		Fn: func(env1 *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			obj := BuiValidate(env1, arg0, arg1)
			if env1.FailureFlag {
				return obj
			}
			switch obj1 := obj.(type) {
			case env.Dict:
				return util.Dict2Context(env1, obj1)
			default:
				return obj1
			}
		},
	},
}
View Source
var ErrorCreationBuiltins = map[string]*env.Builtin{

	"fail": {
		Argsn: 1,
		Doc:   "Creates an error and sets the failure flag, but continues execution (unlike ^fail).",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			ps.FailureFlag = true
			return MakeRyeError(ps, arg0, nil)
		},
	},

	"^fail": {
		Argsn: 1,
		Doc:   "Creates an error and immediately returns from the current function with failure state.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			ps.FailureFlag = true
			ps.ReturnFlag = true
			return MakeRyeError(ps, arg0, nil)
		},
	},

	"refail": {
		Argsn: 2,
		Doc:   "Re-raises an existing error with additional context. TODO -- duplicate of check",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch er := arg0.(type) {
			case *env.Error:
				ps.FailureFlag = true
				return MakeRyeError(ps, arg1, er)
			default:
				return MakeArgError(ps, 1, []env.Type{env.ErrorType}, "reraise")
			}
		},
	},

	"failure": {
		Argsn: 1,
		Doc:   "Creates an error object without setting any flags (unlike fail and ^fail).",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			return MakeRyeError(ps, arg0, nil)
		},
	},

	"empty": {
		Argsn: 0,
		Doc:   "Creates an 404 error object without setting any flags.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			return MakeRyeError(ps, *env.NewInteger(404), nil)
		},
	},

	"failure\\wrap": {
		Argsn: 2,
		Doc:   "Creates a new error that wraps an existing error, allowing for error chaining.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch er := arg1.(type) {
			case *env.Error:
				return MakeRyeError(ps, arg0, er)
			default:
				return MakeArgError(ps, 2, []env.Type{env.ErrorType}, "wrap\\failure")
			}
		},
	},
}

Error Creation Functions

View Source
var ErrorHandlingBuiltins = map[string]*env.Builtin{

	"disarm": {
		AcceptFailure: true,
		Argsn:         1,
		Doc:           "Clears the failure flag while preserving the error object, allowing error inspection without propagation.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			ps.FailureFlag = false
			return arg0
		},
	},

	"check": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Checks if a value is in failure state and wraps it with a new error if so, otherwise returns the original value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			if ps.FailureFlag || arg0.Type() == env.ErrorType {

				switch er := arg0.(type) {
				case *env.Error:
					if er.Status == 0 && er.Message == "" {
						er = nil
					}
					return MakeRyeError(ps, arg1, er)
				case env.Error:

					return MakeRyeError(ps, arg1, &er)
				}
				return env.NewError("Failure flag but value not Failure")
			}
			return arg0
		},
	},

	"^check": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Like 'check' but also sets the return flag to immediately exit the current function.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			if ps.FailureFlag || arg0.Type() == env.ErrorType {
				ps.ReturnFlag = true
				switch er := arg0.(type) {
				case *env.Error:
					if er.Status == 0 && er.Message == "" {
						er = nil
					}
					return MakeRyeError(ps, arg1, er)
				case env.Error:

					return MakeRyeError(ps, arg1, &er)
				}
				return env.NewError("Failure flag but value not Failure")
			}
			return arg0
		},
	},

	"^ensure": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Checks if a value is truthy and returns it if so, otherwise creates an error and immediately returns from the function.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch cond := arg0.(type) {
			case env.Object:
				if !util.IsTruthy(cond) {
					ps.FailureFlag = true
					ps.ReturnFlag = true
					return MakeRyeError(ps, arg1, nil)
				} else {
					return arg0
				}
			}
			return arg0
		},
	},

	"ensure": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Checks if a value is truthy and returns it if so, otherwise creates an error with the failure flag set.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch cond := arg0.(type) {
			case env.Object:
				if !util.IsTruthy(cond) {
					ps.FailureFlag = true
					return MakeRyeError(ps, arg1, nil)
				} else {
					return arg0
				}
			}
			return arg0
		},
	},

	"requires-one-of": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Validates that a value matches one of the provided options, failing with a descriptive error if not.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch optionsBlock := arg1.(type) {
			case env.Block:

				options := optionsBlock.Series.GetAll()
				for _, option := range options {

					if arg0.GetKind() == option.GetKind() && arg0.Inspect(*ps.Idx) == option.Inspect(*ps.Idx) {
						return arg0
					}
				}

				ps.FailureFlag = true

				valueStr := arg0.Inspect(*ps.Idx)

				// Build error message with options
				var errorMsg string
				if len(options) > 0 {
					optionsStr := "{ "
					for i, opt := range options {
						if i > 0 {
							optionsStr += " "
						}
						optionsStr += opt.Inspect(*ps.Idx)
					}
					optionsStr += " }"
					errorMsg = fmt.Sprintf("Value %s must be one of %s", valueStr, optionsStr)
				} else {
					errorMsg = fmt.Sprintf("Value %s does not match any valid option", valueStr)
				}

				return MakeRyeError(ps, *env.NewString(errorMsg), nil)
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 2, []env.Type{env.BlockType}, "requires-one-of")
			}
		},
	},

	"^requires-one-of": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Validates that a value matches one of the provided options, failing with a descriptive error if not.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch optionsBlock := arg1.(type) {
			case env.Block:

				options := optionsBlock.Series.GetAll()
				for _, option := range options {

					if arg0.GetKind() == option.GetKind() && arg0.Inspect(*ps.Idx) == option.Inspect(*ps.Idx) {
						return arg0
					}
				}

				ps.FailureFlag = true

				valueStr := arg0.Inspect(*ps.Idx)

				// Build error message with options
				var errorMsg string
				if len(options) > 0 {
					optionsStr := "{ "
					for i, opt := range options {
						if i > 0 {
							optionsStr += " "
						}
						optionsStr += opt.Inspect(*ps.Idx)
					}
					optionsStr += " }"
					errorMsg = fmt.Sprintf("Value %s must be one of %s", valueStr, optionsStr)
				} else {
					errorMsg = fmt.Sprintf("Value %s does not match any valid option", valueStr)
				}
				ps.ReturnFlag = true
				return MakeRyeError(ps, *env.NewString(errorMsg), nil)
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 2, []env.Type{env.BlockType}, "requires-one-of")
			}
		},
	},

	"ensure\\with": {
		AcceptFailure: true,
		Argsn:         3,
		Doc:           "Injects a value into a validation block and returns the value if the block is truthy, otherwise creates an error.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch bloc := arg1.(type) {
			case env.Block:
				ser := ps.Ser
				ps.Ser = bloc.Series
				EvalBlockInj(ps, arg0, true)
				if ps.ErrorFlag {
					ps.Ser = ser
					return ps.Res
				}
				ps.Ser = ser
				if util.IsTruthy(ps.Res) {
					return arg0
				} else {
					ps.FailureFlag = true
					return MakeRyeError(ps, arg2, nil)
				}
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 2, []env.Type{env.BlockType}, "ensure\\with")
			}
		},
	},

	"^ensure\\with": {
		AcceptFailure: true,
		Argsn:         3,
		Doc:           "Like ensure\\with but also sets the return flag to immediately exit the current function.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch bloc := arg1.(type) {
			case env.Block:
				ser := ps.Ser
				ps.Ser = bloc.Series
				EvalBlockInj(ps, arg0, true)
				if ps.ErrorFlag {
					ps.Ser = ser
					ps.ReturnFlag = true
					return ps.Res
				}
				ps.Ser = ser
				if util.IsTruthy(ps.Res) {
					return arg0
				} else {
					ps.FailureFlag = true
					ps.ReturnFlag = true
					return MakeRyeError(ps, arg2, nil)
				}
			default:
				ps.FailureFlag = true
				ps.ReturnFlag = true
				return MakeArgError(ps, 2, []env.Type{env.BlockType}, "^ensure\\with")
			}
		},
	},

	"fix": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Handles errors by executing a block if the value is in failure state, clearing the failure flag.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			if ps.FailureFlag || arg0.Type() == env.ErrorType {
				ps.FailureFlag = false

				switch bloc := arg1.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					EvalBlockInj(ps, arg0, true)
					MaybeDisplayFailureOrError(ps, ps.Idx, "fix")
					if ps.ErrorFlag {
						ps.Ser = ser
						return ps.Res
					}
					ps.Ser = ser
					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "fix")
				}
			} else {
				return arg0
			}
		},
	},

	"^fix": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Like 'fix' but also sets the return flag to immediately exit the current function with the handler result.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			if ps.FailureFlag || arg0.Type() == env.ErrorType {
				ps.FailureFlag = false

				switch bloc := arg1.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					EvalBlockInj(ps, arg0, true)
					MaybeDisplayFailureOrError(ps, ps.Idx, "^fix")
					ps.Ser = ser
					ps.ReturnFlag = true
					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "^fix")
				}
			} else {
				return arg0
			}
		},
	},

	"fix\\either": {
		AcceptFailure: true,
		Argsn:         3,
		Doc:           "Executes one of two blocks depending on whether the value is in failure state.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			if ps.FailureFlag || arg0.Type() == env.ErrorType {
				ps.FailureFlag = false

				switch bloc := arg1.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					EvalBlockInj(ps, arg0, true)
					MaybeDisplayFailureOrError(ps, ps.Idx, "fix\\either")
					ps.Ser = ser
					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "fix\\either")
				}
			} else {
				switch bloc := arg2.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					EvalBlockInj(ps, arg0, true)
					MaybeDisplayFailureOrError(ps, ps.Idx, "fix\\either")
					ps.Ser = ser
					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 3, []env.Type{env.BlockType}, "fix\\either")
				}
			}
		},
	},

	"fix\\else": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Executes a block if the value is not in failure state, otherwise returns the original value.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			if !(ps.FailureFlag || arg0.Type() == env.ErrorType) {
				ps.FailureFlag = false

				switch bloc := arg1.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					EvalBlockInj(ps, arg0, true)
					MaybeDisplayFailureOrError(ps, ps.Idx, "fix\\else")
					ps.Ser = ser
					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "fix\\else")
				}
			} else {
				return arg0
			}
		},
	},

	"fix\\continue": {
		AcceptFailure: true,
		Argsn:         3,
		Doc:           "Executes one of two blocks depending on whether the value is in failure state, like an error-handling if/else.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			if ps.FailureFlag || arg0.Type() == env.ErrorType {
				ps.FailureFlag = false

				switch bloc := arg1.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					EvalBlockInj(ps, arg0, true)
					ps.Ser = ser
					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "fix\\continue")
				}
			} else {
				switch bloc := arg2.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					EvalBlockInj(ps, arg0, true)
					ps.Ser = ser
					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 3, []env.Type{env.BlockType}, "fix\\continue")
				}
			}
		},
	},

	"continue": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Executes a block only if the value is not in failure state, opposite of 'fix'.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			if !(ps.FailureFlag || arg0.Type() == env.ErrorType) {
				ps.FailureFlag = false

				switch bloc := arg1.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					EvalBlockInj(ps, arg0, true)
					MaybeDisplayFailureOrError(ps, ps.Idx, "continue")
					ps.Ser = ser
					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "continue")
				}
			} else {
				ps.FailureFlag = false
				return arg0
			}
		},
	},

	"^fix\\match": {
		Argsn:         2,
		Doc:           "Error handling switch that matches error codes with handler blocks and sets the return flag.",
		AcceptFailure: true,
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			ps.FailureFlag = false

			switch er := arg0.(type) {
			case env.Error, *env.Error:
				switch bloc := arg1.(type) {
				case env.Block:
					var code env.Object
					any_found := false

					for i := 0; i < bloc.Series.Len(); i += 2 {
						if i > bloc.Series.Len()-2 {
							return MakeBuiltinError(ps, "Switch block malformed.", "^fix\\match")
						}

						switch ev := bloc.Series.Get(i).(type) {
						case env.Integer:
							var status int
							if err, ok := er.(env.Error); ok {
								status = err.Status
							} else if errPtr, ok := er.(*env.Error); ok {
								status = errPtr.Status
							}

							if status == int(ev.Value) {
								any_found = true
								code = bloc.Series.Get(i + 1)
							}
						case env.Void:
							if !any_found {
								code = bloc.Series.Get(i + 1)
								any_found = true
							}
						default:
							return MakeBuiltinError(ps, "Invalid type in block series.", "^fix\\match")
						}
					}

					switch cc := code.(type) {
					case env.Block:

						ser := ps.Ser

						ps.Ser = cc.Series

						EvalBlockInj(ps, arg0, true)

						MaybeDisplayFailureOrError(ps, ps.Idx, "fix\\match")

						ps.Ser = ser

						ps.ReturnFlag = true
						return ps.Res
					default:

						ps.FailureFlag = true
						return MakeBuiltinError(ps, "Malformed switch block.", "^fix\\match")
					}
				default:

					ps.FailureFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "^fix\\match")
				}
			default:
				return arg0
			}
		},
	},

	"try": {
		Argsn: 1,
		Doc:   "Takes a block of code and does (runs) it.",
		Pure:  true,
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch bloc := arg0.(type) {
			case env.Block:
				ser := ps.Ser
				ps.Ser = bloc.Series
				ps.InErrHandler = true
				Eval(ps)

				ps.InErrHandler = false
				ps.ReturnFlag = false
				ps.ErrorFlag = false
				ps.FailureFlag = false

				ps.Ser = ser
				return ps.Res
			default:
				return MakeArgError(ps, 1, []env.Type{env.BlockType}, "try")
			}
		},
	},

	"try-all": {
		Argsn: 1,
		Doc:   "Executes a block and returns a result tuple [success, result], where success is true if no error occurred.",
		Pure:  true,
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch bloc := arg0.(type) {
			case env.Block:

				oldSer := ps.Ser
				oldFailureFlag := ps.FailureFlag
				oldErrorFlag := ps.ErrorFlag
				ps.InErrHandler = true

				ps.Ser = bloc.Series
				Eval(ps)
				MaybeDisplayFailureOrError(ps, ps.Idx, "try-all")

				result := ps.Res
				success := !ps.FailureFlag && !ps.ErrorFlag
				ps.InErrHandler = false

				ps.Ser = oldSer
				ps.FailureFlag = oldFailureFlag
				ps.ErrorFlag = oldErrorFlag

				return *env.NewBlock(*env.NewTSeries([]env.Object{
					*env.NewBoolean(success),
					result,
				}))
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 1, []env.Type{env.BlockType}, "try-all")
			}
		},
	},

	"try\\in": {
		Argsn: 2,
		Doc:   "Takes a Context and a Block. It Does a block inside a given Context.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch ctx := arg0.(type) {
			case *env.RyeCtx:
				switch bloc := arg1.(type) {
				case env.Block:
					ser := ps.Ser
					ps.Ser = bloc.Series
					ps.InErrHandler = true
					EvalBlockInCtxInj(ps, ctx, nil, false)
					MaybeDisplayFailureOrError(ps, ps.Idx, "try\\in")
					ps.InErrHandler = false
					ps.ReturnFlag = false
					ps.ErrorFlag = false
					ps.FailureFlag = false

					ps.Ser = ser
					return ps.Res
				default:
					ps.ErrorFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "try\\in")
				}
			default:
				ps.ErrorFlag = true
				return MakeArgError(ps, 1, []env.Type{env.ContextType}, "try\\in")
			}
		},
	},

	"finally": {
		Argsn: 2,
		Doc:   "Executes a block and ensures another block is executed afterward, regardless of errors.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch mainBlock := arg0.(type) {
			case env.Block:
				switch finallyBlock := arg1.(type) {
				case env.Block:

					oldSer := ps.Ser
					ps.InErrHandler = true

					ps.Ser = mainBlock.Series
					Eval(ps)
					MaybeDisplayFailureOrError(ps, ps.Idx, "finally")

					result := ps.Res
					hasFailure := ps.FailureFlag
					hasError := ps.ErrorFlag

					ps.Ser = finallyBlock.Series
					ps.FailureFlag = false
					ps.ErrorFlag = false
					ps.InErrHandler = false
					Eval(ps)
					MaybeDisplayFailureOrError(ps, ps.Idx, "finally")

					ps.Res = result
					ps.FailureFlag = hasFailure
					ps.ErrorFlag = hasError

					ps.Ser = oldSer

					return ps.Res
				default:
					ps.FailureFlag = true
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "finally")
				}
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 1, []env.Type{env.BlockType}, "finally")
			}
		},
	},

	"retry": {
		Argsn:         2,
		Doc:           "Executes a block and retries it up to N times if it results in a failure.",
		AcceptFailure: true,
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch retries := arg0.(type) {
			case env.Integer:
				switch bloc := arg1.(type) {
				case env.Block:

					ser := ps.Ser

					ps.Ser = bloc.Series
					Eval(ps)
					MaybeDisplayFailureOrError(ps, ps.Idx, "retry")

					if !ps.FailureFlag {
						ps.Ser = ser
						return ps.Res
					}

					result := ps.Res

					for i := int64(1); i < retries.Value; i++ {

						ps.Ser.Reset()
						ps.FailureFlag = false
						ps.ErrorFlag = false

						Eval(ps)
						MaybeDisplayFailureOrError(ps, ps.Idx, "retry")

						if !ps.FailureFlag {
							ps.Ser = ser
							return ps.Res
						}

						result = ps.Res
					}

					ps.Ser = ser
					ps.FailureFlag = true
					return result
				default:
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "retry")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.IntegerType}, "retry")
			}
		},
	},

	"persist": {
		Argsn:         1,
		Doc:           "Executes a block repeatedly until it succeeds (no failure), then returns the successful result. Gives up after 1000 attempts.",
		AcceptFailure: true,
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch bloc := arg0.(type) {
			case env.Block:

				ser := ps.Ser

				for attempt := 1; attempt <= 1000; attempt++ {

					ps.Ser = bloc.Series
					ps.Ser.Reset()
					ps.FailureFlag = false
					ps.ErrorFlag = false

					Eval(ps)
					MaybeDisplayFailureOrError(ps, ps.Idx, "persist")

					if !ps.FailureFlag {
						ps.Ser = ser
						return ps.Res
					}

				}

				ps.Ser = ser
				ps.FailureFlag = true
				return MakeRyeError(ps, *env.NewString("Persist failed: Block did not succeed after 1000 attempts"), nil)
			default:
				return MakeArgError(ps, 1, []env.Type{env.BlockType}, "persist")
			}
		},
	},

	"timeout": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Executes a block of code with a timeout, failing if execution exceeds the specified duration in milliseconds.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch ms := arg0.(type) {
			case env.Integer:
				switch bloc := arg1.(type) {
				case env.Block:

					ser := ps.Ser

					resultChan := make(chan env.Object, 1)
					doneChan := make(chan bool, 1)

					psCopy := env.NewProgramStateOLD(bloc.Series, ps.Idx)
					psCopy.Ctx = ps.Ctx
					psCopy.PCtx = ps.PCtx
					psCopy.Gen = ps.Gen

					go func() {
						Eval(psCopy)
						MaybeDisplayFailureOrError(ps, ps.Idx, "timeout")

						resultChan <- psCopy.Res
						doneChan <- psCopy.FailureFlag
					}()

					timeoutDuration := time.Duration(ms.Value) * time.Millisecond

					select {
					case result := <-resultChan:
						ps.FailureFlag = <-doneChan
						ps.Ser = ser
						return result

					case <-time.After(timeoutDuration):
						ps.FailureFlag = true
						ps.Ser = ser
						return MakeRyeError(ps, *env.NewString(fmt.Sprintf("Execution timed out after %d ms", ms.Value)), nil)
					}
				default:
					return MakeArgError(ps, 2, []env.Type{env.BlockType}, "timeout")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.IntegerType}, "timeout")
			}
		},
	},
}

Error Handling Functions

View Source
var ErrorInspectionBuiltins = map[string]*env.Builtin{

	"is-error": {
		AcceptFailure: true,
		Argsn:         1,
		Doc:           "Returns true if the value is an error, false otherwise.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			ps.FailureFlag = false
			switch arg0.(type) {
			case env.Error, *env.Error:
				return *env.NewBoolean(true)
			}
			return *env.NewBoolean(false)
		},
	},

	"error-kind?": {
		AcceptFailure: true,
		Argsn:         1,
		Doc:           "Returns the kind of an error, or void if not an error.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			ps.FailureFlag = false
			switch err := arg0.(type) {
			case env.Error:
				return *env.NewWord(err.Kind.Index)
			case *env.Error:
				return *env.NewWord(err.Kind.Index)
			}
			return *env.NewVoid()
		},
	},

	"is-error-of-kind": {
		AcceptFailure: true,
		Argsn:         2,
		Doc:           "Returns true if the value is an error of the specified kind, false otherwise.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			ps.FailureFlag = false

			// Get the kind to check
			var kindIndex int
			switch kind := arg1.(type) {
			case env.Word:
				kindIndex = kind.Index
			case env.Tagword:
				kindIndex = kind.Index
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 2, []env.Type{env.WordType, env.TagwordType}, "is-error-of-kind")
			}

			switch err := arg0.(type) {
			case env.Error:
				return *env.NewBoolean(err.Kind.Index == kindIndex)
			case *env.Error:
				return *env.NewBoolean(err.Kind.Index == kindIndex)
			}

			return *env.NewBoolean(false)
		},
	},

	"cause?": {
		AcceptFailure: true,
		Argsn:         1,
		Doc:           "Extracts the root cause from an error chain by traversing the Parent references.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch er := arg0.(type) {
			case env.Error:

				current := &er
				for current.Parent != nil {
					current = current.Parent
				}
				return *current
			case *env.Error:

				current := er
				for current.Parent != nil {
					current = current.Parent
				}
				return *current
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 1, []env.Type{env.ErrorType}, "cause?")
			}
		},
	},

	"status?": {
		AcceptFailure: true,
		Argsn:         1,
		Doc:           "Extracts the numeric status code from an error object.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch er := arg0.(type) {
			case env.Error:
				return *env.NewInteger(int64(er.Status))
			case *env.Error:
				return *env.NewInteger(int64(er.Status))
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 1, []env.Type{env.ErrorType}, "status?")
			}
		},
	},

	"message?": {
		AcceptFailure: true,
		Argsn:         1,
		Doc:           "Extracts the message string from an error object.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch er := arg0.(type) {
			case env.Error:
				return *env.NewString(er.Message)
			case *env.Error:
				return *env.NewString(er.Message)
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 1, []env.Type{env.ErrorType}, "message?")
			}
		},
	},

	"details?": {
		AcceptFailure: true,
		Argsn:         1,
		Doc:           "Extracts additional details from an error object as a dictionary.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			var originalMap map[string]env.Object
			switch er := arg0.(type) {
			case env.Error:
				originalMap = er.Values
			case *env.Error:
				originalMap = er.Values
			default:
				ps.FailureFlag = true
				return MakeArgError(ps, 1, []env.Type{env.ErrorType}, "details?")
			}

			convertedMap := make(map[string]any)
			for key, value := range originalMap {
				convertedMap[key] = value
			}
			return env.NewDict(convertedMap)
		},
	},

	"has-failed": {
		AcceptFailure: true,
		Argsn:         1,
		Doc:           "Tests if a value is an error object, returning true for errors and false for non-errors.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			ps.FailureFlag = false
			switch arg0.(type) {
			case env.Error, *env.Error:
				return *env.NewBoolean(true)
			}
			return *env.NewBoolean(false)
		},
	},

	"is-failure": {
		Argsn: 1,
		Doc:   "Checks if a value is a failure/error type. Unlike has-failed, this doesn't accept failures - the value must already be disarmed.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch arg0.(type) {
			case env.Error, *env.Error:
				return *env.NewBoolean(true)
			}
			return *env.NewBoolean(false)
		},
	},

	"is-success": {
		Argsn: 1,
		Doc:   "Returns true for any value that is not a failure/error type. The opposite of is-failure.",
		Fn: func(ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object {
			switch arg0.(type) {
			case env.Error, *env.Error:
				return *env.NewBoolean(false)
			}
			return *env.NewBoolean(true)
		},
	},
}

Error Inspection Functions

View Source
var NoInspectMode bool

NoInspectMode controls whether to exit immediately on error without showing debugging options

View Source
var OfferDebuggingOptionsHook func(es *env.ProgramState, genv *env.Idxs, tag string) = func(es *env.ProgramState, genv *env.Idxs, tag string) {}

OfferDebuggingOptionsHook is a function variable that can be set by the console package to provide interactive debugging options. It defaults to a no-op, which is the correct behaviour for embedded/WASM/b_norepl builds that never import the console package.

View Source
var ShowResults bool
View Source
var VarBuiltins_demo = map[string]*env.VarBuiltin{

	"var-not": {
		Argsn: 1,
		Doc:   "Performs logical negation on boolean values only.",
		Pure:  true,
		Fn: func(env1 *env.ProgramState, args ...env.Object) env.Object {
			switch b := args[0].(type) {
			case env.Boolean:
				return *env.NewBoolean(!b.Value)
			default:
				return MakeArgError(env1, 1, []env.Type{env.BooleanType}, "not")
			}
		},
	},

	"var-and": {
		Argsn: 2,
		Doc:   "Performs a logical AND operation between two boolean values, or a bitwise AND operation between two integer values.",
		Pure:  true,
		Fn: func(ps *env.ProgramState, args ...env.Object) env.Object {
			switch s1 := args[0].(type) {
			case env.Boolean:
				switch s2 := args[1].(type) {
				case env.Boolean:
					return *env.NewBoolean(s1.Value && s2.Value)
				default:
					return MakeArgError(ps, 2, []env.Type{env.BooleanType}, "and")
				}
			case env.Integer:
				switch s2 := args[1].(type) {
				case env.Integer:
					return *env.NewInteger(s1.Value & s2.Value)
				default:
					return MakeArgError(ps, 2, []env.Type{env.IntegerType}, "and")
				}
			default:
				return MakeArgError(ps, 1, []env.Type{env.BooleanType, env.IntegerType}, "and")
			}
		},
	},
}

Functions

func AddIndexes

func AddIndexes(ps *env.ProgramState, s *env.Table, columns []env.Word) env.Object

func AutoType

func AutoType(ps *env.ProgramState, s *env.Table, percent float64) env.Object

func BuiValidate

func BuiValidate(env1 *env.ProgramState, arg0 env.Object, arg1 env.Object) env.Object

func CallBuiltin_CollectArgs added in v0.0.90

func CallBuiltin_CollectArgs(bi env.Builtin, ps *env.ProgramState, arg0_ env.Object, toLeft bool, pipeSecond bool, firstVal env.Object, opword bool, dotword bool)

CallBuiltin_CollectArgs calls a builtin by collecting up to 5 arguments from the code stream. Called from: EvalExpression_DispatchType, EvalObject Purpose: Main builtin caller - collects arguments, handles failure flags, calls builtin function

func CallCurriedCaller added in v0.0.82

func CallCurriedCaller(cc env.CurriedCaller, ps *env.ProgramState, arg0_ env.Object, toLeft bool, pipeSecond bool, firstVal env.Object, opword bool, dotword bool)

CallCurriedCaller handles calling a curried caller (partially applied function or builtin). Called from: EvalExpression_DispatchType, EvalObject Purpose: Executes curried callers by filling in remaining arguments and calling the underlying builtin/function

func CallCurriedCallerArgsN added in v0.2.0

func CallCurriedCallerArgsN(cc env.CurriedCaller, ps *env.ProgramState, args ...env.Object)

CallCurriedCallerArgsN calls a curried caller with N provided arguments (not collected from code stream). Called from: builtins that need to call curried callers with specific arguments (e.g., HTTP handlers) Purpose: Fills curried caller's nil slots with provided arguments and executes the underlying function/builtin

func CallFunctionArgs1 added in v0.2.7

func CallFunctionArgs1(fn env.Function, ps *env.ProgramState, arg0 env.Object, ctx *env.RyeCtx)

CallFunctionArgs1 calls a function with exactly 1 argument provided. Called from: CallFunctionWithArgs, builtins needing to call 1-arg functions Purpose: Optimized path for 1-argument function calls from builtins

func CallFunctionArgs2

func CallFunctionArgs2(fn env.Function, ps *env.ProgramState, arg0 env.Object, arg1 env.Object, ctx *env.RyeCtx)

CallFunctionArgs2 calls a function with exactly 2 arguments provided. Called from: CallFunctionWithArgs, builtins needing to call 2-arg functions Purpose: Optimized path for 2-argument function calls from builtins

func CallFunctionArgs4

func CallFunctionArgs4(fn env.Function, ps *env.ProgramState, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, ctx *env.RyeCtx)

CallFunctionArgs4 calls a function with exactly 4 arguments provided. Called from: CallFunctionWithArgs, builtins needing to call 4-arg functions Purpose: Optimized path for 4-argument function calls from builtins

func CallFunctionArgsN added in v0.0.21

func CallFunctionArgsN(fn env.Function, ps *env.ProgramState, ctx *env.RyeCtx, args ...env.Object)

CallFunctionArgsN calls a function with a variable number of arguments (N arguments). Called from: CallFunctionWithArgs, CallCurriedCaller, builtins with variable args Purpose: Generic function caller for any number of arguments provided as a slice

func CallFunctionWithArgs added in v0.0.81

func CallFunctionWithArgs(fn env.Function, ps *env.ProgramState, ctx *env.RyeCtx, args ...env.Object)

CallFunctionWithArgs is a consolidated function caller that dispatches based on argument count. Called from: Various builtins that need to call user functions Purpose: Dispatcher that routes to specialized function callers based on number of arguments (0,1,2,4,N) Note: Context creation is handled by the subfunctions, not here

func CallFunction_CollectArgs added in v0.0.90

func CallFunction_CollectArgs(fn env.Function, ps *env.ProgramState, arg0_ env.Object, toLeft bool, ctx *env.RyeCtx, pipeSecond ...interface{})

CallFunction_CollectArgs calls a function by collecting arguments from the code stream. Called from: EvalObject, CallFunctionWithArgs (0 arg case) Purpose: Main function caller in evaluator - collects args from code, sets up context, executes function body

func CallVarBuiltin added in v0.0.40

func CallVarBuiltin(bi env.VarBuiltin, ps *env.ProgramState, arg0_ env.Object, toLeft bool, pipeSecond bool, firstVal env.Object, opword bool, dotword bool)

CallVarBuiltin calls a variadic builtin by collecting all required arguments into a slice. Called from: EvalExpression_DispatchType, EvalObject Purpose: Handles builtins with variable number of arguments, collecting them into a slice

func CheckForFailureWithBuiltin added in v0.2.50

func CheckForFailureWithBuiltin(bi env.Builtin, ps *env.ProgramState, n int) bool

CheckForFailureWithBuiltin is an exported wrapper around checkForFailureWithBuiltin, exposed for use by the batteries package.

func ColNames added in v0.0.31

func ColNames(ps *env.ProgramState, from env.Object, fnName string) ([]string, env.Object)

func CompileRyeToEyr added in v0.0.21

func CompileRyeToEyr(block *env.Block, ps *env.ProgramState, eyrBlock *env.Block) *env.Block

func CompileStepRyeToEyr added in v0.0.21

func CompileStepRyeToEyr(block *env.Block, ps *env.ProgramState, eyrBlock *env.Block) *env.Block

func CompileWord added in v0.0.21

func CompileWord(block *env.Block, ps *env.ProgramState, word env.Word, eyrBlock *env.Block)

func CountWhereBlock added in v0.2.0

func CountWhereBlock(ps *env.ProgramState, s *env.Table, name string, block env.Block) env.Object

func CountWhereBlockMultiCol added in v0.2.0

func CountWhereBlockMultiCol(ps *env.ProgramState, s *env.Table, cols env.Block, code env.Block) env.Object

func CountWhereBuiltin added in v0.2.0

func CountWhereBuiltin(ps *env.ProgramState, s *env.Table, name string, builtin env.Builtin) env.Object

func CountWhereFunction added in v0.2.0

func CountWhereFunction(ps *env.ProgramState, s *env.Table, name string, fn env.Function) env.Object

func DetermineContext added in v0.0.22

func DetermineContext(fn env.Function, ps *env.ProgramState, ctx *env.RyeCtx) (*env.RyeCtx, bool)

DetermineContext determines the appropriate context for a function call. Called from: CallFunctionWithArgs, CallFunctionArgs1, CallFunctionArgs2, CallFunctionArgs4, CallFunctionArgsN Purpose: Sets up function execution context based on pure/impure, defined context, and parent context Returns: The context to use and a boolean indicating if it was obtained from the pool (and can be returned)

func DirectlyCallBuiltin

func DirectlyCallBuiltin(ps *env.ProgramState, bi env.Builtin, a0 env.Object, a1 env.Object) env.Object

DirectlyCallBuiltin directly calls a builtin with provided arguments (no collection from code). Called from: Builtins that need to call other builtins directly Purpose: Direct builtin invocation helper for inter-builtin calls

func DisplayEnhancedError added in v0.0.86

func DisplayEnhancedError(es *env.ProgramState, genv *env.Idxs, tag string, topLevel bool)

DisplayEnhancedError displays a formatted error with source location and block context. Called from: MaybeDisplayFailureOrError2 Purpose: Main error display - shows red banner, error message, block location, and <here> marker

func DisplayRyeValue added in v0.2.50

func DisplayRyeValue(ps *env.ProgramState, arg0 env.Object, interactive bool) (env.Object, string)

DisplayRyeValue is a non-interactive stub: it just returns the value as-is. The full interactive version (using the term package) lives in builtins_base_printing.go which is excluded under no_baseio.

func Distinct added in v0.2.0

func Distinct(ps *env.ProgramState, s *env.Table, colNames []string) env.Object

func DropColumn added in v0.0.24

func DropColumn(ps *env.ProgramState, s env.Table, name env.String) env.Object

func DropColumnBlock added in v0.0.24

func DropColumnBlock(ps *env.ProgramState, s env.Table, names env.Block) env.Object

func DropColumns added in v0.0.24

func DropColumns(ps *env.ProgramState, s env.Table, names []env.String) env.Object

Drop one or more columns from a table, returning a new table

func Eval added in v0.2.3

func Eval(ps *env.ProgramState)

EvalBlock is the main entry point for evaluating a block of code. Called from: Throughout the codebase - main evaluation loops, builtins, function calls Purpose: Dispatches to the appropriate dialect-specific evaluator (Rye2, Eyr, Rye0, Rye00) HOTCODE: Performance-critical function called frequently during execution

func EvalBlock

func EvalBlock(ps *env.ProgramState, blk *env.Block)

EvalBlock sets the block as the current series and evaluates it. This is the idiomatic entry point for embedders - combining ps.SetBlock and Eval into a single call:

blk := loader.LoadString(src, false, ps)
evaldo.EvalBlock(ps, &blk.(env.Block))

func EvalBlockInCtxInj

func EvalBlockInCtxInj(ps *env.ProgramState, ctx *env.RyeCtx, inj env.Object, injnow bool)

EvalBlockInCtxInj evaluates a block in a specific context with value injection. Called from: Builtins that need to evaluate code in a different context Purpose: Temporarily switches to specified context, evaluates block, then restores original context

func EvalBlockInj

func EvalBlockInj(ps *env.ProgramState, inj env.Object, injnow bool)

EvalBlockInj evaluates a block with value injection support across multiple dialects. Called from: EvalBlock, EvalExpression_DispatchType (for OPGROUP/OPBLOCK modes), observer execution, builtins Purpose: Main multi-dialect block evaluator with optional value injection into the first expression HOTCODE: Performance-critical function called frequently during execution

func EvalBlockInj_Rye2 added in v0.2.0

func EvalBlockInj_Rye2(ps *env.ProgramState, inj env.Object, injnow bool)

EvalBlockInj_Rye2 evaluates a block of code with optional value injection for Rye2 dialect. Called from: EvalBlockInj (multi-dialect dispatcher), CallFunction_CollectArgs, ExecuteDeferredBlocks Purpose: Core Rye2 evaluator - loops through expressions, handling injection, commas, and error/failure flags

func EvalDataPath added in v0.2.60

func EvalDataPath(ps *env.ProgramState, dp env.DataPath)

EvalDataPath evaluates a data path value (word.0."key".word). Called from: EvalExpression_DispatchType Purpose: The first segment is the subject word, which is looked up in the current context (like a word). The remaining segments are literal accessors (keys/indexes) used to retrieve nested values from blocks, lists, dicts, contexts, tables, etc. It mirrors the `->` (get) extraction semantics with 0-based indexing.

func EvalExpression added in v0.0.81

func EvalExpression(ps *env.ProgramState, inj env.Object, injnow bool, limited bool, opword bool, dotword bool) bool

EvalExpression is the consolidated expression evaluator handling both regular and injected evaluation. Called from: EvalExpressionInj, EvalExpression_CollectArg, EvalExpressionInjLimited (wrapper functions) Purpose: Evaluates left side via DispatchType, then optionally evaluates right side (opwords/pipewords)

func EvalExpressionInj

func EvalExpressionInj(ps *env.ProgramState, inj env.Object, injnow bool) bool

EvalExpressionInj evaluates an expression with optional value injection. Called from: EvalBlockInj, EvalExpression_DispatchType (OPBBLOCK mode), EvalSetword, EvalModword Purpose: Wrapper for EvalExpression that allows injecting a value into the expression

func EvalExpressionInjLimited

func EvalExpressionInjLimited(ps *env.ProgramState, inj env.Object, injnow bool) bool

EvalExpressionInjLimited evaluates an expression with injection in limited mode (stops at setwords/pipewords). Called from: Various places where expression evaluation should not consume setwords or pipewords Purpose: Limited expression evaluation that prevents consuming certain right-side constructs

func EvalExpression_CollectArg added in v0.0.90

func EvalExpression_CollectArg(ps *env.ProgramState, limited bool, opword bool)

EvalExpression_CollectArg evaluates an expression without injection (for collecting function arguments). Called from: CallFunction_CollectArgs, CallBuiltin_CollectArgs, CallVarBuiltin, EvalWord Purpose: Wrapper for EvalExpression used when collecting arguments from code Note: dotword=false here - callers that need dotword blocking use EvalExpression directly with dotword=true

func EvalExpression_DispatchType added in v0.0.90

func EvalExpression_DispatchType(ps *env.ProgramState)

EvalExpression_DispatchType is the core type dispatcher that evaluates individual Rye values. Called from: EvalExpression, EvalWord, EvalGenword Purpose: Main type switch - handles all Rye value types and dispatches to appropriate handlers Note: This is the heart of the evaluator - if Rye were fully Polish notation, this would be most of it It handles the case when there is no value on the left, so we are starting a fresh expression

func EvalGenword

func EvalGenword(ps *env.ProgramState, word env.Genword, leftVal env.Object, toLeft bool)

EvalGenword evaluates a generic word (explicitly declared generic). Called from: EvalExpression_DispatchType Purpose: Handles words explicitly marked as generic - evaluates next expression and dispatches on its type

func EvalGetword

func EvalGetword(ps *env.ProgramState, word env.Getword, leftVal env.Object, toLeft bool)

EvalGetword evaluates a get-word (prefixed with ?) which retrieves a value without calling it. Called from: EvalExpression_DispatchType Purpose: Returns the raw value associated with a word without evaluating it (like quoting)

func EvalModword added in v0.0.20

func EvalModword(ps *env.ProgramState, word env.Modword)

EvalModword evaluates a mod-word (word::) which modifies an existing word in the context. Called from: EvalExpression_DispatchType Purpose: Evaluates the expression to the right and modifies an existing word's value, triggers observers

func EvalObject

func EvalObject(ps *env.ProgramState, object env.Object, leftVal env.Object, toLeft bool, ctx *env.RyeCtx, pipeSecond bool, firstVal env.Object, opword bool, dotword bool)

EvalObject evaluates a Rye object, particularly handling callable types (builtins, functions). Called from: EvalWord, EvalGenword Purpose: Evaluates found objects - dispatches builtins/functions/cpaths to their callers, returns other types

func EvalSetword

func EvalSetword(ps *env.ProgramState, word env.Setword)

EvalSetword evaluates a set-word (word:) which sets a new word in the context. Called from: EvalExpression_DispatchType Purpose: Evaluates the expression to the right and binds the result to a word (creating new binding)

func EvalToBoolean added in v0.2.60

func EvalToBoolean(val any) (any, env.Object)

func EvalToDecimal added in v0.2.60

func EvalToDecimal(val any) (any, env.Object)

func EvalToInteger added in v0.2.60

func EvalToInteger(val any) (any, env.Object)

The following wrappers expose validation helpers moved from batteries to evaldo

func EvalWord

func EvalWord(ps *env.ProgramState, word env.Object, leftVal env.Object, toLeft bool, pipeSecond bool, opword bool, dotword bool)

EvalWord evaluates a word by looking up its value and potentially dispatching to generic words. Called from: EvalExpression_DispatchType, OptionallyEvalExpressionRight, EvalObject Purpose: Main word evaluator - looks up in context, tries generic words if not found, handles getcpath mode

when there is a left value

func EvaluateLoadedValue added in v0.0.20

func EvaluateLoadedValue(ps *env.ProgramState, block_ env.Object, script_ string, allowMod bool) env.Object

func ExecuteDeferredBlocks added in v0.0.40

func ExecuteDeferredBlocks(ps *env.ProgramState)

ExecuteDeferredBlocks executes all deferred blocks in LIFO order (last in, first out). Called from: CallFunction_CollectArgs, CallFunctionArgs1, CallFunctionArgs2, CallFunctionArgs4, CallFunctionArgsN (via defer) Purpose: Executes cleanup blocks registered with defer, similar to Go's defer statement

func ExtractFunctionName added in v0.0.93

func ExtractFunctionName(doc string) (name string, found bool)

ExtractFunctionName extracts the function name from a docstring that ends with "(name)" If the pattern is found, returns the name; otherwise returns the full docstring

func FastCallBuiltin added in v0.0.44

func FastCallBuiltin(ps *env.ProgramState, bi env.Builtin, arg0 env.Object, arg1 env.Object, arg2 env.Object, arg3 env.Object, arg4 env.Object) env.Object

FastCallBuiltin is an optimized version of CallBuiltin for the common case where all arguments are already available. It skips the argument evaluation, error checking, and currying logic that makes CallBuiltin slower.

This function should be used when: 1. All arguments are already available (no need to evaluate from program state) 2. No currying is needed 3. The builtin function has a fixed number of arguments

func FastCallFunction added in v0.0.80

func FastCallFunction(fn env.Function, ps *env.ProgramState, args []env.Object, ctx *env.RyeCtx) *env.ProgramState

FastCallFunction is an optimized version of Rye0_CallFunction for the common case where all arguments are already available. It skips the complex context determination, argument evaluation, and error checking that makes Rye0_CallFunction slower.

This function should be used when: 1. All arguments are already available (no need to evaluate from program state) 2. The function has a fixed number of arguments (typically small, like 0-3) 3. No complex context path resolution is needed

func FastCallFunctionWithArgs added in v0.0.80

func FastCallFunctionWithArgs(fn env.Function, ps *env.ProgramState, ctx *env.RyeCtx, args ...env.Object) *env.ProgramState

FastCallFunctionWithArgs is an optimized version of Rye0_CallFunctionWithArgs It's similar to FastCallFunction but follows the signature of Rye0_CallFunctionWithArgs

func FillVoid added in v0.2.0

func FillVoid(ps *env.ProgramState, s *env.Table, colName string, fillValue env.Object) env.Object

func FindWordValue added in v0.2.50

func FindWordValue(ps *env.ProgramState, word1 env.Object) (bool, env.Object, *env.RyeCtx)

FindWordValue is an exported wrapper around findWordValue, exposed for use by the batteries package.

func FormatBacktickQuotes added in v0.0.93

func FormatBacktickQuotes(message string) string

FormatBacktickQuotes processes an error message and highlights text within backticks with a special background color for terminal display. Called from: DisplayEnhancedError Purpose: Converts `word` to highlighted word for better error readability

func FormatBuiltinReference added in v0.0.93

func FormatBuiltinReference(doc string) string

FormatBuiltinReference formats a builtin reference for error display If a name can be extracted from the docstring, it returns the emphasized name Otherwise returns the full docstring Uses backticks to quote the name - the display function will highlight them

func GenerateColumn

func GenerateColumn(ps *env.ProgramState, s env.Table, name env.Word, extractCols env.Block, code env.Block) env.Object

func GenerateColumnRegexReplace added in v0.0.17

func GenerateColumnRegexReplace(ps *env.ProgramState, s *env.Table, name env.Word, fromColName env.Word, re *regexp.Regexp, pattern string) env.Object

func GetNumRowsFrom added in v0.0.31

func GetNumRowsFrom(ps *env.ProgramState, data any) (int, *env.Error)

func GreaterThanNew added in v0.2.50

func GreaterThanNew(arg0 env.Object, arg1 env.Object) bool

GreaterThanNew is an exported wrapper around greaterThanNew, exposed for use by the batteries package.

func GroupBy added in v0.0.17

func GroupBy(ps *env.ProgramState, s env.Table, cols []string, aggregations map[string][]string) env.Object

func IntersectBlocksCustom added in v0.0.34

func IntersectBlocksCustom(a env.Block, b env.Block, ps *env.ProgramState, fn env.Function) []env.Object

func IntersectStringsCustom added in v0.0.34

func IntersectStringsCustom(a env.String, b env.String, ps *env.ProgramState, fn env.Function) string

func LeftJoin added in v0.0.17

func LeftJoin(ps *env.ProgramState, s1 env.Table, s2 env.Table, col1 string, col2 string, innerJoin bool) env.Object

func LesserThanNew added in v0.2.50

func LesserThanNew(arg0 env.Object, arg1 env.Object) bool

LesserThanNew is an exported wrapper around lesserThanNew, exposed for use by the batteries package.

func LoadColumnData added in v0.0.31

func LoadColumnData(ps *env.ProgramState, data any, colIdx int, numRows int, colData []env.TableRow, cols []string) *env.Error

func LoadScriptLocalFile added in v0.0.20

func LoadScriptLocalFile(ps *env.ProgramState, s1 env.Uri) (env.Object, string)

func MakeArgError

func MakeArgError(env1 *env.ProgramState, N int, typ []env.Type, fn string) *env.Error

func MakeArgError2 added in v0.2.61

func MakeArgError2(env1 *env.ProgramState, N int, typ []env.Type, fn string, got env.Object) *env.Error

MakeArgError2 is like MakeArgError but also reports the actual type of the provided value

func MakeArgErrorMessage added in v0.0.25

func MakeArgErrorMessage(N int, allowedTypes []env.Type, fn string) string

func MakeBuiltinError

func MakeBuiltinError(env1 *env.ProgramState, msg string, fn string) *env.Error

func MakeColError added in v0.0.31

func MakeColError(ps *env.ProgramState, builtinName string, colName string, expectedRowCount int, actualRowCount int) *env.Error

func MakeError

func MakeError(env1 *env.ProgramState, msg string) *env.Error

func MakeNativeArgError added in v0.0.19

func MakeNativeArgError(env1 *env.ProgramState, N int, knd []string, fn string) *env.Error

func MakeNeedsThawedArgError added in v0.0.25

func MakeNeedsThawedArgError(env1 *env.ProgramState, fn string) *env.Error

func MakeRyeError

func MakeRyeError(env1 *env.ProgramState, val env.Object, er *env.Error) *env.Error

func MaybeAcceptComma

func MaybeAcceptComma(ps *env.ProgramState, inj env.Object, injnow bool) bool

MaybeAcceptComma handles comma expression guards between block-level expressions. Called from: EvalBlockInj, EvalExpression_DispatchType (OPBBLOCK mode) Purpose: Checks for and consumes comma separators; re-enables injection if comma found HOTPATH: Called frequently during block evaluation

func MaybeDisplayFailureOrError

func MaybeDisplayFailureOrError(es *env.ProgramState, genv *env.Idxs, tag string)

MaybeDisplayFailureOrError displays errors/failures if error flag is set (wrapper). Called from: Throughout the codebase after evaluation operations Purpose: Wrapper that calls MaybeDisplayFailureOrError2 with default parameters

func MaybeDisplayFailureOrError2 added in v0.0.87

func MaybeDisplayFailureOrError2(es *env.ProgramState, genv *env.Idxs, tag string, topLevel bool, fileMode bool)

MaybeDisplayFailureOrError2 displays errors/failures and optionally offers debugging options. Called from: MaybeDisplayFailureOrError, main REPL/file execution code Purpose: Main error display coordinator - shows enhanced errors and offers debugging in file mode

func MaybeDisplayFailureOrErrorWASM added in v0.0.13

func MaybeDisplayFailureOrErrorWASM(es *env.ProgramState, genv *env.Idxs, printfn func(string), tag string)

MaybeDisplayFailureOrErrorWASM displays errors/failures in WASM environment using custom print function. Called from: WASM build code (main_wasm.go) Purpose: WASM-specific error display that uses provided print function instead of fmt.Println

func Mth_EvalDirect added in v0.2.0

func Mth_EvalDirect(ps *env.ProgramState) env.Object

Mth_EvalDirect is the fast single-pass math evaluator. It reads tokens from ps.Ser using the Shunting-yard algorithm and computes the result inline. No builtin lookup, no global state, no init.

func NameOfRyeType added in v0.0.25

func NameOfRyeType(t env.Type) string

func OptionallyEvalExpressionRight added in v0.0.90

func OptionallyEvalExpressionRight(nextObj env.Object, ps *env.ProgramState, limited bool, allowOpwords bool, allowDotwords bool)

OptionallyEvalExpressionRight evaluates right-side constructs like opwords, pipewords, and setwords. Called from: EvalExpression, recursively from itself Purpose: Handles operator precedence by evaluating opwords/pipewords/setwords/modwords to the right allowOpwords=false: prevents nested opwords from being consumed as arguments (set when collecting opword/dotword args) allowDotwords=false: prevents nested dotwords from being consumed as arguments (set when collecting dotword args)

func RegisterBaseBuiltins added in v0.2.50

func RegisterBaseBuiltins(ps *env.ProgramState)

RegisterBaseBuiltins registers only the pure-computation builtins that have no OS / file-system / shell dependency. This is the right choice for embedding Rye inside a Go application when you want to keep the sandbox free of host-OS access. Call RegisterBaseIOBuiltins afterwards if you also want file, shell, args, and exit support.

func RegisterBaseIOBuiltins added in v0.2.50

func RegisterBaseIOBuiltins(ps *env.ProgramState)

RegisterBaseIOBuiltins is DEPRECATED - OS / file / shell / args builtins have moved to the baseio package (github.com/refaktor/rye/baseio). This function is now a no-op kept for backward compatibility. Call baseio.Register(ps) after RegisterBuiltins(ps) to add I/O support.

func RegisterBuiltin added in v0.2.6

func RegisterBuiltin(ps *env.ProgramState, word string, builtin env.Builtin)

TODO -- remove registerBuiltin

func RegisterBuiltinGroups added in v0.2.3

func RegisterBuiltinGroups(ps *env.ProgramState, groups ...string)

RegisterBuiltinGroups registers all builtins belonging to the named groups. Group names match the identifiers used in RegisterBuiltins (e.g. "base", "io", "http", "math"). Multiple groups may be passed; each is registered in order.

Example:

evaldo.RegisterBuiltinGroups(ps, "base", "io", "http")

func RegisterBuiltins

func RegisterBuiltins(ps *env.ProgramState)

RegisterBuiltins registers the base set: pure-computation builtins plus basic printing. OS I/O and interactive display builtins are registered separately via baseio.Register(ps). For the full standard set including batteries, also call:

baseio.Register(ps)
batteries.RegisterBatteries(ps)

func RegisterBuiltins2

func RegisterBuiltins2(builtins map[string]*env.Builtin, ps *env.ProgramState, name string)

func RegisterBuiltinsFilter added in v0.2.3

func RegisterBuiltinsFilter(ps *env.ProgramState, names []string)

RegisterBuiltinsFilter registers only the specific named builtins from the full builtin set. Each entry in names must be the plain word as it appears in Rye code (e.g. "add", "print", "http-get"). For context-namespaced groups (math, os, crypto, …) the word is still given without the context prefix; the function creates the child context automatically and places the word there.

Example:

evaldo.RegisterBuiltinsFilter(ps, []string{"add", "subtract", "print", "if", "fn", "either"})

func RegisterBuiltinsInContext added in v0.0.16

func RegisterBuiltinsInContext(builtins map[string]*env.Builtin, ps *env.ProgramState, name string) *env.RyeCtx

func RegisterBuiltinsInSubContext added in v0.0.23

func RegisterBuiltinsInSubContext(builtins map[string]*env.Builtin, ps *env.ProgramState, parent *env.RyeCtx, name string) *env.RyeCtx

func RegisterVarBuiltins added in v0.0.40

func RegisterVarBuiltins(ps *env.ProgramState)

func RegisterVarBuiltins2 added in v0.0.40

func RegisterVarBuiltins2(builtins map[string]*env.VarBuiltin, ps *env.ProgramState, name string)

func RenameColumn added in v0.0.32

func RenameColumn(ps *env.ProgramState, s *env.Table, oldName env.String, newName env.String) env.Object

Drop one or more columns from a table, returning a new table

func ReturnContextToPool added in v0.2.50

func ReturnContextToPool(fnCtx *env.RyeCtx, fromPool bool)

ReturnContextToPool is an exported wrapper around returnContextToPool, exposed for use by the batteries package.

func Rye0_CallBuiltin added in v0.0.36

func Rye0_CallBuiltin(bi env.Builtin, ps *env.ProgramState, arg0_ env.Object, toLeft bool, pipeSecond bool, firstVal env.Object) *env.ProgramState

Rye0_CallBuiltin calls a builtin function. Parameters:

  • bi: The builtin to call
  • ps: The program state
  • arg0_: The first argument (if any)
  • toLeft: Whether the call is to the left
  • pipeSecond: Whether this is a pipe second call
  • firstVal: The first value (if any)

Returns:

  • The updated program state

func Rye0_CallFunction added in v0.0.36

func Rye0_CallFunction(fn env.Function, ps *env.ProgramState, arg0 env.Object, toLeft bool, ctx *env.RyeCtx) *env.ProgramState

Rye0_CallFunction calls a function. Parameters:

  • fn: The function to call
  • ps: The program state
  • arg0: The first argument (if any)
  • toLeft: Whether the call is to the left
  • ctx: The context (if any)

Returns:

  • The updated program state

func Rye0_CallFunctionWithArgs added in v0.0.37

func Rye0_CallFunctionWithArgs(fn env.Function, ps *env.ProgramState, ctx *env.RyeCtx, args ...env.Object) *env.ProgramState

Rye0_CallFunctionWithArgs calls a function with the given arguments. This consolidates the functionality of Rye0_CallFunctionArgs2, Rye0_CallFunctionArgs4, and Rye0_CallFunctionArgsN. Parameters:

  • fn: The function to call
  • ps: The program state
  • ctx: The context (if any)
  • args: The arguments to pass to the function

Returns:

  • The updated program state

func Rye0_CallFunction_Optimized added in v0.0.80

func Rye0_CallFunction_Optimized(fn env.Function, ps *env.ProgramState, arg0 env.Object, toLeft bool, ctx *env.RyeCtx) *env.ProgramState

Modified version of Rye0_CallFunction that uses FastCallFunction for common cases

func Rye0_DetermineContext added in v0.0.36

func Rye0_DetermineContext(fn env.Function, ps *env.ProgramState, ctx *env.RyeCtx) *env.RyeCtx

Rye0_DetermineContext determines the appropriate context for a function call. Parameters:

  • fn: The function
  • ps: The program state
  • ctx: The context (if any)

Returns:

  • The determined context

func Rye0_EvalBlockInj added in v0.0.36

func Rye0_EvalBlockInj(ps *env.ProgramState, inj env.Object, injnow bool) *env.ProgramState

Rye0_EvalBlockInj evaluates a block with an optional injected value.

func Rye0_EvalDataPath added in v0.2.60

func Rye0_EvalDataPath(ps *env.ProgramState, dp env.DataPath) *env.ProgramState

Rye0_EvalDataPath evaluates a data path value (word.0."key".word). The first segment is the subject word; the remaining segments are literal accessors used to retrieve nested values from blocks, lists, dicts, contexts, tables, etc.

func Rye0_EvalExpressionInj added in v0.0.36

func Rye0_EvalExpressionInj(ps *env.ProgramState, inj env.Object, injnow bool) (*env.ProgramState, bool)

Rye0_EvalExpressionInj evaluates an expression with an optional injected value.

func Rye0_EvalExpression_CollectArg added in v0.0.90

func Rye0_EvalExpression_CollectArg(ps *env.ProgramState, limited bool) *env.ProgramState

Rye0_EvalExpression_CollectArg evaluates an expression with optional limitations.

func Rye0_EvalExpression_DispatchType added in v0.0.90

func Rye0_EvalExpression_DispatchType(ps *env.ProgramState) *env.ProgramState

Rye0_EvalExpression_DispatchType evaluates a concrete expression. This is the main part of the evaluator that handles all Rye value types.

func Rye0_EvalGenword added in v0.0.36

func Rye0_EvalGenword(ps *env.ProgramState, word env.Genword, leftVal env.Object, toLeft bool) *env.ProgramState

Rye0_EvalGenword evaluates a generic word.

func Rye0_EvalGetword added in v0.0.36

func Rye0_EvalGetword(ps *env.ProgramState, word env.Getword, leftVal env.Object, toLeft bool) *env.ProgramState

Rye0_EvalGetword evaluates a get-word.

func Rye0_EvalModword added in v0.0.36

func Rye0_EvalModword(ps *env.ProgramState, word env.Modword) *env.ProgramState

Rye0_EvalModword evaluates a mod-word.

func Rye0_EvalObject added in v0.0.36

func Rye0_EvalObject(ps *env.ProgramState, object env.Object, leftVal env.Object, toLeft bool, ctx *env.RyeCtx, pipeSecond bool, firstVal env.Object) *env.ProgramState

Rye0_EvalObject evaluates a Rye object.

func Rye0_EvalSetword added in v0.0.36

func Rye0_EvalSetword(ps *env.ProgramState, word env.Setword) *env.ProgramState

Rye0_EvalSetword evaluates a set-word.

func Rye0_EvalWord added in v0.0.36

func Rye0_EvalWord(ps *env.ProgramState, word env.Object, leftVal env.Object, toLeft bool, pipeSecond bool) *env.ProgramState

Rye0_EvalWord evaluates a word in the current context.

func Rye0_EvaluateBlock added in v0.0.37

func Rye0_EvaluateBlock(ps *env.ProgramState, block env.Block) *env.ProgramState

Rye0_EvaluateBlock handles the evaluation of a block object.

func Rye0_FastEvalBlock added in v0.0.43

func Rye0_FastEvalBlock(ps *env.ProgramState) *env.ProgramState

Rye0_FastEvalBlock evaluates a block of code using the fast evaluator

func Rye0_MaybeDisplayFailureOrError added in v0.0.36

func Rye0_MaybeDisplayFailureOrError(es *env.ProgramState, genv *env.Idxs, tag string)

Rye0_MaybeDisplayFailureOrError displays failure or error information if present. Parameters:

  • es: The program state
  • genv: The index environment
  • tag: A tag for the error message

func Rye0_MaybeDisplayFailureOrErrorWASM added in v0.0.36

func Rye0_MaybeDisplayFailureOrErrorWASM(es *env.ProgramState, genv *env.Idxs, printfn func(string), tag string)

Rye0_MaybeDisplayFailureOrErrorWASM displays failure or error information for WASM environment. Parameters:

  • es: The program state
  • genv: The index environment
  • printfn: The print function to use
  • tag: A tag for the error message

func Rye0_checkContextErrorHandler added in v0.0.36

func Rye0_checkContextErrorHandler(ps *env.ProgramState) bool

Rye0_checkContextErrorHandler checks if there is an error handler defined in the context. Parameters:

  • ps: The program state

Returns:

  • Whether an error handler was found and executed

func Rye0_checkErrorFlag added in v0.0.81

func Rye0_checkErrorFlag(ps *env.ProgramState) bool

Rye0_checkErrorFlag checks if there are error or return flags.

func Rye0_checkFlagsAfterExpression added in v0.0.81

func Rye0_checkFlagsAfterExpression(ps *env.ProgramState) bool

Rye00_checkFlagsAfterExpression checks if there are failure flags after evaluating a block.

func Rye0_checkForFailureWithBuiltin added in v0.0.81

func Rye0_checkForFailureWithBuiltin(bi env.Builtin, ps *env.ProgramState, n int) bool

Rye0_checkForFailureWithBuiltin checks if there are failure flags and handles them appropriately.

func Rye0_findCPathValue added in v0.0.37

func Rye0_findCPathValue(ps *env.ProgramState, word env.CPath) (bool, env.Object, *env.RyeCtx)

Rye0_findCPathValue handles the lookup of context path values. It traverses nested contexts and dicts (using word-name string keys), and supports '@' parent-context navigation (represented as '_@' words).

func Rye0_findWordValue added in v0.0.36

func Rye0_findWordValue(ps *env.ProgramState, word1 env.Object) (bool, env.Object, *env.RyeCtx)

Rye0_findWordValue returns the value associated with a word in the current context. It also checks if the word refers to a builtin in a parent context, and if so, it can replace the word with the builtin directly in the series for faster future lookups.

func Rye00_CallBuiltin added in v0.0.81

func Rye00_CallBuiltin(bi env.Builtin, ps *env.ProgramState, arg0_ env.Object, toLeft bool, pipeSecond bool, firstVal env.Object)

Rye00_CallBuiltin calls a builtin function. Optimized version that focuses on performance.

func Rye00_EvalBlockInj added in v0.0.81

func Rye00_EvalBlockInj(ps *env.ProgramState, inj env.Object, injnow bool) *env.ProgramState

Rye00_EvalBlockInj evaluates a block with an optional injected value.

func Rye00_EvalExpression_DispatchType added in v0.0.90

func Rye00_EvalExpression_DispatchType(ps *env.ProgramState)

Rye00_EvalExpression_DispatchType evaluates a concrete expression. This is the main part of the evaluator that handles only integers and builtins.

func Rye00_EvalObject added in v0.0.81

func Rye00_EvalObject(ps *env.ProgramState, object env.Object, leftVal env.Object, toLeft bool, ctx *env.RyeCtx, pipeSecond bool, firstVal env.Object)

Rye00_EvalObject evaluates a Rye object. Simplified version that only handles builtins.

func Rye00_EvalWord added in v0.0.81

func Rye00_EvalWord(ps *env.ProgramState, word env.Object, leftVal env.Object, toLeft bool, pipeSecond bool)

Rye00_EvalWord evaluates a word in the current context. Simplified version that only handles builtins.

func Rye00_MaybeDisplayFailureOrError added in v0.0.81

func Rye00_MaybeDisplayFailureOrError(es *env.ProgramState, genv *env.Idxs, tag string)

Rye00_MaybeDisplayFailureOrError displays failure or error information if present.

func Rye00_checkFlagsAfterExpression added in v0.0.81

func Rye00_checkFlagsAfterExpression(ps *env.ProgramState) bool

Rye00_checkFlagsAfterExpression checks if there are failure flags after evaluating a block.

func Rye00_checkForFailureWithBuiltin added in v0.0.81

func Rye00_checkForFailureWithBuiltin(bi env.Builtin, ps *env.ProgramState, n int) bool

Rye00_checkForFailureWithBuiltin checks if there are failure flags and handles them appropriately.

func Rye00_findWordValue added in v0.0.81

func Rye00_findWordValue(ps *env.ProgramState, word1 env.Object) (bool, env.Object, *env.RyeCtx)

Rye00_findWordValue returns the value associated with a word in the current context. Simplified version that only looks for builtins.

func RyeValueToTableRow added in v0.0.33

func RyeValueToTableRow(spr *env.Table, obj env.Object) (bool, string, *env.TableRow)

func SheetFromColumns added in v0.0.31

func SheetFromColumns(ps *env.ProgramState, arg0 env.Object, arg1 env.Object) (res env.Object)

func SheetFromColumnsMapData added in v0.0.31

func SheetFromColumnsMapData(ps *env.ProgramState, cols []string, arg1 env.Object) env.Object

func SortByColumn

func SortByColumn(ps *env.ProgramState, s *env.Table, name string)

func SortByColumnDesc

func SortByColumnDesc(ps *env.ProgramState, s *env.Table, name string)

func TableRowsFromBlockOrList added in v0.0.33

func TableRowsFromBlockOrList(ps *env.ProgramState, spr *env.Table, numCols int, arg1 any) (*env.TableRow, *env.Error)

func Trace added in v0.2.50

func Trace(s string)

Trace is an exported wrapper around trace, exposed for use by the batteries package.

func TriggerObservers added in v0.0.88

func TriggerObservers(ps *env.ProgramState, ctx *env.RyeCtx, wordIndex int, oldValue, newValue env.Object)

TriggerObservers triggers all observers watching a variable that has changed. Called from: EvalModword, OptionallyEvalExpressionRight (LModword case) Purpose: Notifies observers when a variable's value changes, executing their observer blocks

func TryHandleFailure added in v0.2.50

func TryHandleFailure(ps *env.ProgramState) bool

TryHandleFailure is an exported wrapper around tryHandleFailure, exposed for use by the batteries package.

func Validation_EvalBlock

func Validation_EvalBlock(es *env.ProgramState, vals env.Dict) (env.Dict, map[string]env.Object)

func Validation_EvalBlock_Context added in v0.0.85

func Validation_EvalBlock_Context(es *env.ProgramState, vals *env.RyeCtx) (*env.RyeCtx, map[string]env.Object)

func Validation_EvalBlock_List

func Validation_EvalBlock_List(es *env.ProgramState, vals env.List) (env.Object, []env.Object)

func Validation_EvalBlock_Value added in v0.0.85

func Validation_EvalBlock_Value(es *env.ProgramState, val env.Object) (env.Object, env.Object)

func WhereBetween added in v0.0.17

func WhereBetween(ps *env.ProgramState, s *env.Table, name string, val1 env.Object, val2 env.Object, inclusiveMode bool) env.Object

func WhereBlock added in v0.2.0

func WhereBlock(ps *env.ProgramState, s *env.Table, name string, block env.Block) env.Object

func WhereBlockMultiCol added in v0.2.0

func WhereBlockMultiCol(ps *env.ProgramState, s *env.Table, cols env.Block, code env.Block) env.Object

func WhereBuiltin added in v0.2.0

func WhereBuiltin(ps *env.ProgramState, s *env.Table, name string, builtin env.Builtin) env.Object

func WhereContains added in v0.0.17

func WhereContains(ps *env.ProgramState, s *env.Table, name string, val string, not bool) env.Object

func WhereEquals

func WhereEquals(ps *env.ProgramState, s *env.Table, name string, val env.Object) env.Object

func WhereFunction added in v0.2.0

func WhereFunction(ps *env.ProgramState, s *env.Table, name string, fn env.Function) env.Object

func WhereGreater

func WhereGreater(ps *env.ProgramState, s *env.Table, name string, val env.Object) env.Object

func WhereIn added in v0.0.17

func WhereIn(ps *env.ProgramState, s *env.Table, name string, b []env.Object) env.Object

func WhereLesser

func WhereLesser(ps *env.ProgramState, s *env.Table, name string, val env.Object) env.Object

func WhereMatch added in v0.0.17

func WhereMatch(ps *env.ProgramState, s *env.Table, name string, r *regexp.Regexp) env.Object

func WhereNotEquals added in v0.0.82

func WhereNotEquals(ps *env.ProgramState, s *env.Table, name string, val env.Object) env.Object

func WhereNotIn added in v0.0.87

func WhereNotIn(ps *env.ProgramState, s *env.Table, name string, b []env.Object) env.Object

Types

type Instruction added in v0.0.43

type Instruction func(vm *Rye0VM) int

Instruction is a function that executes a single instruction in the VM

func Rye0_CompileExpression added in v0.0.43

func Rye0_CompileExpression(ps *env.ProgramState) Instruction

Rye0_CompileExpression compiles a single expression into an Instruction

type PersistentCtxInterface added in v0.2.50

type PersistentCtxInterface interface {
	env.Context
}

PersistentCtxInterface is the interface evaldo's base builtins use to interact with a PersistentCtx without depending on its concrete type. The batteries package's PersistentCtx struct satisfies this interface.

type Program added in v0.0.43

type Program struct {
	// contains filtered or unexported fields
}

Program represents a compiled Rye0 program

func Rye0_CompileBlock added in v0.0.43

func Rye0_CompileBlock(ps *env.ProgramState) *Program

Rye0_CompileBlock compiles a block of code into a Program

type Rye0VM added in v0.0.43

type Rye0VM struct {
	Sp int // Exported for use in benchmarks
	// contains filtered or unexported fields
}

Rye0VM represents a virtual machine for executing compiled Rye0 code

func NewRye0VM added in v0.0.43

func NewRye0VM(ctx *env.RyeCtx, pctx *env.RyeCtx, gen *env.Gen, idx *env.Idxs) *Rye0VM

NewRye0VM creates a new virtual machine for executing compiled Rye0 code

func (*Rye0VM) Execute added in v0.0.43

func (vm *Rye0VM) Execute(p *Program) (env.Object, error)

Execute executes a compiled program

func (*Rye0VM) Pop added in v0.0.43

func (vm *Rye0VM) Pop() env.Object

Pop pops a value from the stack

func (*Rye0VM) Push added in v0.0.43

func (vm *Rye0VM) Push(val env.Object)

Push pushes a value onto the stack

type RyeBlockCustomSort added in v0.0.34

type RyeBlockCustomSort struct {
	// contains filtered or unexported fields
}

Custom Sort object interface

func (RyeBlockCustomSort) Len added in v0.0.34

func (s RyeBlockCustomSort) Len() int

func (RyeBlockCustomSort) Less added in v0.0.34

func (s RyeBlockCustomSort) Less(i, j int) bool

func (RyeBlockCustomSort) Swap added in v0.0.34

func (s RyeBlockCustomSort) Swap(i, j int)

type RyeBlockSort

type RyeBlockSort []env.Object

Sort object interface

func (RyeBlockSort) Len

func (s RyeBlockSort) Len() int

func (RyeBlockSort) Less

func (s RyeBlockSort) Less(i, j int) bool

func (RyeBlockSort) Swap

func (s RyeBlockSort) Swap(i, j int)

type RyeListCustomSort added in v0.0.34

type RyeListCustomSort struct {
	// contains filtered or unexported fields
}

Custom Sort object interface

func (RyeListCustomSort) Len added in v0.0.34

func (s RyeListCustomSort) Len() int

func (RyeListCustomSort) Less added in v0.0.34

func (s RyeListCustomSort) Less(i, j int) bool

func (RyeListCustomSort) Swap added in v0.0.34

func (s RyeListCustomSort) Swap(i, j int)

type RyeListSort

type RyeListSort []any

Sort list interface

func (RyeListSort) Len

func (s RyeListSort) Len() int

func (RyeListSort) Less

func (s RyeListSort) Less(i, j int) bool

func (RyeListSort) Swap

func (s RyeListSort) Swap(i, j int)

type RyeStringSort added in v0.0.35

type RyeStringSort []rune

Sort list interface

func (RyeStringSort) Len added in v0.0.35

func (s RyeStringSort) Len() int

func (RyeStringSort) Less added in v0.0.35

func (s RyeStringSort) Less(i, j int) bool

func (RyeStringSort) Swap added in v0.0.35

func (s RyeStringSort) Swap(i, j int)

type ValidationError

type ValidationError struct {
	// contains filtered or unexported fields
}

Integer represents an integer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL