├── CHANNELS.md ├── DESIGN.md ├── GOALS.md ├── README.md ├── REF.md ├── TESTS.md ├── apl ├── README.md ├── a │ ├── commands.go │ ├── help.go │ ├── register.go │ └── runtime.go ├── apl.go ├── array.go ├── array_test.go ├── assign.go ├── big │ ├── bigfloat │ │ ├── LICENSE.md │ │ ├── README │ │ ├── exp.go │ │ ├── log.go │ │ ├── misc.go │ │ ├── pow.go │ │ └── sqrt.go │ ├── complex.go │ ├── float.go │ ├── int.go │ ├── rat.go │ └── register.go ├── bool.go ├── channel.go ├── domain.go ├── domain │ ├── array.go │ ├── channel.go │ ├── compare.go │ ├── domain.go │ ├── functions.go │ ├── identifier.go │ ├── image.go │ ├── list.go │ ├── numeric.go │ ├── object.go │ ├── operator.go │ ├── scalar.go │ ├── string.go │ └── type.go ├── error.go ├── eval.go ├── fmt.go ├── fmt_test.go ├── function.go ├── http │ └── register.go ├── image.go ├── index.go ├── int.go ├── io │ ├── README.md │ ├── env.go │ ├── fs.go │ ├── mount.go │ ├── read.go │ ├── register.go │ └── var.go ├── lambda.go ├── list.go ├── multiline.go ├── numbers │ ├── complex.go │ ├── complexs.go │ ├── exception.go │ ├── float.go │ ├── floats.go │ ├── numbers_test.go │ ├── register.go │ ├── time.go │ └── times.go ├── object.go ├── op.go ├── operators │ ├── assign.go │ ├── at.go │ ├── axis.go │ ├── commute.go │ ├── dot.go │ ├── each.go │ ├── identity.go │ ├── jot.go │ ├── power.go │ ├── rank.go │ ├── reduce.go │ ├── register.go │ ├── select.go │ └── stencil.go ├── parse.go ├── parse_test.go ├── primitives │ ├── apl_test.go │ ├── array.go │ ├── axis.go │ ├── boolean.go │ ├── channel.go │ ├── comma.go │ ├── compare.go │ ├── decode.go │ ├── dict.go │ ├── doc_test.go │ ├── domino.go │ ├── elementary.go │ ├── enclose.go │ ├── find.go │ ├── format.go │ ├── gen.go │ ├── grade.go │ ├── index.go │ ├── iota.go │ ├── match.go │ ├── query.go │ ├── register.go │ ├── reverse.go │ ├── rho.go │ ├── selection.go │ ├── table.go │ ├── tack.go │ ├── take.go │ ├── transpose.go │ ├── type.go │ ├── uniform_test.go │ └── unique.go ├── register.go ├── rpc │ ├── README.md │ ├── init.go │ ├── register.go │ ├── rpc.go │ └── server.go ├── scan │ ├── scan.go │ └── scan_test.go ├── string.go ├── strings │ └── register.go ├── table.go ├── tower.go ├── train.go ├── uniform.go ├── value.go ├── var.go └── xgo │ ├── convert.go │ ├── function.go │ ├── method.go │ ├── register.go │ └── type.go ├── build ├── cmd ├── apl.go ├── apl │ ├── README.md │ ├── apl_test.go │ ├── main.go │ └── testdata │ │ ├── a.apl │ │ ├── a.out │ │ ├── abc.apl │ │ ├── abc.out │ │ ├── e.apl │ │ ├── e.err │ │ ├── finn.apl │ │ ├── finn.out │ │ ├── fmt.apl │ │ ├── fmt.out │ │ ├── obj.apl │ │ ├── obj.out │ │ ├── parse.apl │ │ ├── parse.out │ │ ├── table.apl │ │ └── table.out ├── iv.go ├── iv │ ├── README.md │ ├── iv_test.go │ ├── main.go │ └── testdata │ │ ├── a.iv │ │ ├── a.out │ │ ├── colsum.iv │ │ ├── colsum.out │ │ ├── rank.iv │ │ ├── rank.out │ │ ├── shape.iv │ │ └── shape.out └── testing.go ├── go.mod └── log.svg /GOALS.md: -------------------------------------------------------------------------------- 1 | # APL\iv 2 | APL\iv is an implementation of APL written in go, depending only on the standard library. 3 | 4 | As such, it is a package that can be included into any go project. 5 | This may be as small as `cmd/apl` or a huge application where APL simply acts as a hidden debugging interface with all it's expressive powers. 6 | 7 | - extendable and embeddable 8 | - extend APL by implementing extensions in go 9 | - embed APL by spreading it over already existing go programs 10 | - *If that sounds too complicated and uses too many words:* `(APL\iv÷Go)←→Lua÷C` 11 | - compatibility and portability: it runs everywhere go runs 12 | - cross compiling from x to y out of the box, e.g. `GOOS=linux GOARCH=mipsle go install` working on any host including windows is a matter of course 13 | 14 | # Goals 15 | - #1: Build the smallest APL machine. Small with respect to m³ 16 | 17 | # Compatibility 18 | - The compatibility goal is to be mostly conforming to APL2/Dyalog core language substracting nested arrays 19 | - The parser adds some more restrictions 20 | - function variables have to be lowercase: `f←+/`, nouns are uppercase 21 | - lambdas (dfns) exist but no user defined operators directly in APL (go extension can define operators) 22 | - minor issues: 23 | - `/\ etc` are implemented as operators. These are really nasty. 24 | - assignment is also implemented as an operator. But `{indexed, modified, selective}` assignment should work. 25 | 26 | # Non-compatibility 27 | - Workspace, Namespace, Quadfunctions, I-Beams, user functions others than lambdas. 28 | All this does not exist, is or will be done differently. 29 | 30 | # Additions 31 | - Replaceable numeric tower 32 | - bool and int (index) is always there as `apl.Bool` and `apl.Index` 33 | - the default tower is compiled in with `numbers.Register(a)` and provides `int64, float64, complex128, time (time.Time + time.Duration)` 34 | - big numbers can be used with `big.Register(a)` which adds two more towers: 35 | - big: bigint, bigrat 36 | - precise: bigfloat, bigcomplex 37 | - towers are separated, they cannot be mixed within an expression, but can be changed at runtime (if they are compiled in) 38 | - `big→set 1` 39 | - Overloading primitives and operators 40 | - adding the packages `primitives.Register(a)` and `operators.Register(a)` adds the default implementation to the interpreter. 41 | - a user package (e.g. to interface any application dependend go type) may overload each primitive function or operator. 42 | - Lists 43 | - Instead of nested arrays, there is a list type similar to K's. 44 | - A list needs a terminating semicolon: `(1;2;3;)` or nested `(1;(2;3;);4;5;)` 45 | - Dicts and tables 46 | - Also influence by K are dictionaries and tables (and the time type mentioned above). 47 | - A `Dict` is a special implementation of the more general `Object` 48 | - Go interface 49 | - go structs are mapped into APL by implementing an `Object`. Working on the from APL side feels like working on a Dict. 50 | - struct methods are mapped to fields of the dict that have function type. 51 | - package xgo `xgo.Register(a)` adds helpers to translate from go to apl types. 52 | - Streaming 53 | - the type `Channel` combines two go channels for sequential reading and writing of any `apl.Value` to a concurrent process, rpc call or go routine 54 | 55 | # Speed and size 56 | Of course it should be fast and compact. However the primary goal is implementation speed. This is hard enough. 57 | 58 | # Disclaimer 59 | The author has never used APL. It's a chicken and egg problem. 60 | Primary source for the implementation is the APL2 Language Reference, Dyalog 17 Language Reference guide and on some occasions the ISO spec. All testing has been done with tryapl.org. Thank you a lot, Dyalog for this. 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APL\iv interpreter and stream processor 2 |

3 |
4 |

5 | 6 | # contents 7 | - [apl: an extendable and embeddable APL interpreter written in go](apl) 8 | - Documentation 9 | - [REF.md](REF.md) reference of all standard primitives and operators 10 | - [TESTS.md](TESTS.md) output of test coverage which gives an overview of the state of affairs 11 | - [GOALS.md](GOALS.md) describes the target of the project 12 | - [DESIGN.md](DESIGN.md) description of the go implementation and how to write extra packages 13 | 14 | # programs 15 | - [cmd/apl](cmd/apl): APL interpreter as a command line program 16 | - [cmd/iv](cmd/iv): a program similar to awk with an APL backend but for streaming n-dimensional data 17 | 18 | # A random loop through pattern space 19 | ``` 20 | ⎕IO←0 21 | j←{(⍳2*⍺){⎕←(?∘⍴⌷⊢)⍸~1↓⌽0,(⍺⍴2)⊤⍵}⍣≡1⍴⍵} ⍝ not done.. 22 | ``` 23 | -------------------------------------------------------------------------------- /apl/README.md: -------------------------------------------------------------------------------- 1 | # APL\iv - core package 2 | 3 | This is the core package of the interpreter. 4 | 5 | ## Usage: 6 | 7 | go get github.com/ktye/iv/apl/... 8 | 9 | ```go 10 | // import github.com/ktye/iv/apl 11 | 12 | a := apl.New(os.Stdout) 13 | numbers.Register(a) 14 | primitives.Register(a) 15 | operators.Register(a) 16 | 17 | err := a.ParseAndEval("⍳3") 18 | ``` 19 | 20 | The core package and all additional packages in it's subdirectories require only the Go standard library. 21 | Extra packages with external dependencies can be found in `iv/aplextra`. 22 | 23 | ## Packages 24 | - [a](a/) access to the go runtime 25 | - [big](big/) big numbers as an alternative 26 | - [io](io/) filesystem access 27 | - [rpc](rpc/) remote procedure calls and ipc communication 28 | - [strings](strings/) wrapper of go strings library 29 | - [xgo](xgo/) generic interface to go types 30 | -------------------------------------------------------------------------------- /apl/a/commands.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/ktye/iv/apl" 7 | "github.com/ktye/iv/apl/numbers" 8 | "github.com/ktye/iv/apl/scan" 9 | ) 10 | 11 | // toCommand attaches the Rewrite method to the function. 12 | type toCommand func([]scan.Token) []scan.Token 13 | 14 | func (f toCommand) Rewrite(t []scan.Token) []scan.Token { 15 | return f(t) 16 | } 17 | 18 | func symbol(s string) scan.Token { 19 | return scan.Token{T: scan.Symbol, S: s} 20 | } 21 | 22 | // rw0 is a scan.Command that rewrite the symbol with a 0 argument. 23 | // Example: 24 | // /q is rewritten to a→q 0 25 | type rw0 string 26 | 27 | func (r rw0) Rewrite(t []scan.Token) []scan.Token { 28 | sym := scan.Token{T: scan.Identifier, S: "a→" + string(r)} 29 | num := scan.Token{T: scan.Number, S: "0"} 30 | tokens := make([]scan.Token, len(t)+2) 31 | tokens[0] = sym 32 | tokens[1] = num 33 | copy(tokens[2:], t) 34 | return tokens 35 | } 36 | 37 | // printvar prints a string representation of the value. 38 | // If the value is a string that is a valid variable name, it is dereferenced. 39 | // This allows to print the definition of lambda functions. 40 | func printvar(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 41 | s, ok := R.(apl.String) 42 | if !ok { 43 | return apl.String(R.String(a.Format)), nil 44 | } 45 | v := a.Lookup(string(s)) 46 | if v == nil { 47 | return s, nil 48 | } 49 | return apl.String(v.String(a.Format)), nil 50 | } 51 | 52 | func printCmd(t []scan.Token) []scan.Token { 53 | return append([]scan.Token{scan.Token{T: scan.Identifier, S: "a→p"}}, t...) 54 | } 55 | 56 | // Timer is used to time an expression. It is called by the rewrite command /t 57 | // If the argument is a time, it returns the elapsed duration since that time. 58 | // Otherwise it returns the current time. 59 | func timer(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 60 | t, ok := R.(numbers.Time) 61 | if !ok { 62 | return numbers.Time(time.Now()), nil 63 | } 64 | dt := time.Since(time.Time(t)) 65 | y0, _ := time.Parse("15h04", "00h00") // see apl/numbers/time.go 66 | return numbers.Time(y0.Add(dt)), nil 67 | } 68 | 69 | // timeCmd rewrites the tokens to calculate the duration. 70 | // T__← a→t 0 ⋄ [TOKENS] ⋄ a→t T__ 71 | func timeCmd(t []scan.Token) []scan.Token { 72 | t__ := scan.Token{T: scan.Identifier, S: "T__"} 73 | asn := symbol("←") 74 | tim := scan.Token{T: scan.Identifier, S: "a→t"} 75 | num := scan.Token{T: scan.Number, S: "0"} 76 | dia := scan.Token{T: scan.Diamond, S: "⋄"} 77 | 78 | tokens := []scan.Token{t__, asn, tim, num, dia} 79 | tokens = append(tokens, t...) 80 | return append(tokens, dia, tim, t__) 81 | } 82 | -------------------------------------------------------------------------------- /apl/a/help.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | 8 | "github.com/ktye/iv/apl" 9 | ) 10 | 11 | // help returns the help text in a channel. 12 | func help(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 13 | var buf bytes.Buffer 14 | fmt.Fprintf(&buf, "Commands:") 15 | for _, c := range a.Scanner.Commands() { 16 | fmt.Fprintf(&buf, " %s", c) 17 | } 18 | fmt.Fprintf(&buf, "\n\n") 19 | 20 | a.Doc(&buf) 21 | return apl.LineReader(ioutil.NopCloser(&buf)), nil 22 | } 23 | -------------------------------------------------------------------------------- /apl/a/register.go: -------------------------------------------------------------------------------- 1 | // Package a provides an interface to the internals of the interpreter and the go runtime. 2 | // 3 | // Some of the functions defined return information about the go runtime. 4 | // This includes the parent application if the interpreter is built-in. 5 | // 6 | // c 0 return number of CPUs 7 | // g 0 return number of go routines 8 | // m 0 return runtime.MemStats as a dictionary 9 | // v 0 return go version 10 | package a 11 | 12 | import ( 13 | "fmt" 14 | "os" 15 | 16 | "github.com/ktye/iv/apl" 17 | "github.com/ktye/iv/apl/scan" 18 | ) 19 | 20 | // Register adds the a package to the interpreter. 21 | func Register(p *apl.Apl, name string) { 22 | if name == "" { 23 | name = "a" 24 | } 25 | pkg := map[string]apl.Value{ 26 | "c": apl.ToFunction(cpus), 27 | "g": apl.ToFunction(goroutines), 28 | "h": apl.ToFunction(help), 29 | "m": apl.ToFunction(Memstats), 30 | "p": apl.ToFunction(printvar), 31 | "q": apl.ToFunction(quit), 32 | "t": apl.ToFunction(timer), 33 | "v": apl.ToFunction(goversion), 34 | } 35 | cmd := map[string]scan.Command{ 36 | "h": rw0("h"), 37 | "p": toCommand(printCmd), 38 | "q": rw0("q"), 39 | "t": toCommand(timeCmd), 40 | } 41 | p.AddCommands(cmd) 42 | p.RegisterPackage(name, pkg) 43 | } 44 | 45 | // Quit accepts a string or a number. 46 | // Nonempty strings return an exit code != 0 and print the error to stderr. 47 | // Numbers are used as exit code. 48 | func quit(p *apl.Apl, _, R apl.Value) (apl.Value, error) { 49 | if n, ok := R.(apl.Number); ok { 50 | if idx, ok := n.ToIndex(); ok == false { 51 | return nil, fmt.Errorf("a q: exit code must be convertible to int") 52 | } else { 53 | os.Exit(idx) 54 | } 55 | } 56 | if s, ok := R.(apl.String); ok { 57 | if s == "" { 58 | os.Exit(0) 59 | } else { 60 | fmt.Fprintln(os.Stderr, s) 61 | os.Exit(1) 62 | } 63 | } 64 | return nil, fmt.Errorf("a q: argument must be a string or an int: %T", R) 65 | } 66 | -------------------------------------------------------------------------------- /apl/a/runtime.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | import ( 4 | "reflect" 5 | "runtime" 6 | 7 | "github.com/ktye/iv/apl" 8 | "github.com/ktye/iv/apl/xgo" 9 | ) 10 | 11 | // Memstats returns runtime.MemStats as an object. 12 | func Memstats(p *apl.Apl, L, R apl.Value) (apl.Value, error) { 13 | var m runtime.MemStats 14 | runtime.ReadMemStats(&m) 15 | return xgo.Convert(reflect.ValueOf(m)) 16 | } 17 | 18 | func cpus(p *apl.Apl, _, R apl.Value) (apl.Value, error) { 19 | return apl.Int(runtime.NumCPU()), nil 20 | } 21 | 22 | func goroutines(p *apl.Apl, _, R apl.Value) (apl.Value, error) { 23 | return apl.Int(runtime.NumGoroutine()), nil 24 | } 25 | 26 | func goversion(p *apl.Apl, _, R apl.Value) (apl.Value, error) { 27 | return apl.String(runtime.Version()), nil 28 | } 29 | -------------------------------------------------------------------------------- /apl/apl.go: -------------------------------------------------------------------------------- 1 | // Package apl implements an APL interpreter. 2 | package apl 3 | 4 | import ( 5 | "io" 6 | "io/ioutil" 7 | "reflect" 8 | 9 | "github.com/ktye/iv/apl/scan" 10 | ) 11 | 12 | // New starts a new interpreter. 13 | func New(w io.Writer) *Apl { 14 | a := Apl{ 15 | stdout: w, 16 | env: newEnv(), 17 | Origin: 1, 18 | Format: Format{Fmt: make(map[reflect.Type]string)}, 19 | //PP: 0, 20 | //Fmt: make(map[reflect.Type]string), 21 | primitives: make(map[Primitive][]PrimitiveHandler), 22 | operators: make(map[string][]Operator), 23 | symbols: make(map[rune]string), 24 | pkg: make(map[string]*env), 25 | } 26 | a.parser.a = &a 27 | return &a 28 | } 29 | 30 | // Apl stores the interpreter state. 31 | type Apl struct { 32 | scan.Scanner 33 | Format Format 34 | parser 35 | stdout io.Writer 36 | stdimg ImageWriter 37 | Tower Tower 38 | Origin int 39 | //PP int 40 | //Fmt map[reflect.Type]string 41 | env *env 42 | primitives map[Primitive][]PrimitiveHandler 43 | operators map[string][]Operator 44 | symbols map[rune]string 45 | pkg map[string]*env 46 | scaninit bool 47 | } 48 | 49 | type Format struct { 50 | PP int 51 | Fmt map[reflect.Type]string 52 | } 53 | 54 | // LoadPkg loads a package from a file. 55 | // It temporarily removes the current environment, executes the package file with EvalFile 56 | // and stores the resulting environment in a package with the name of pkg. 57 | func (a *Apl) LoadPkg(r io.Reader, file string, pkg string) (err error) { 58 | save := a.env 59 | a.env = newEnv() 60 | defer func() { 61 | a.env = save 62 | }() 63 | 64 | err = a.EvalFile(r, file) 65 | if err != nil { 66 | return err 67 | } 68 | a.pkg[pkg] = a.env 69 | return nil 70 | } 71 | 72 | // Parse parses a line of input into the current context. 73 | // It returns a Program which can be evaluated. 74 | func (a *Apl) Parse(line string) (Program, error) { 75 | tokens, err := a.Scan(line) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | p, err := a.parse(tokens) 81 | if err != nil { 82 | return nil, err 83 | } else { 84 | return p, nil 85 | } 86 | } 87 | 88 | func (a *Apl) ParseAndEval(line string) error { 89 | if p, err := a.Parse(line); err != nil { 90 | return err 91 | } else { 92 | return a.Eval(p) 93 | } 94 | } 95 | 96 | func (a *Apl) Scan(line string) ([]scan.Token, error) { 97 | // On the first call, the scanner needs to be told all symbols that 98 | // have been registered. 99 | if a.scaninit == false { 100 | m := make(map[rune]string) 101 | for r, s := range a.symbols { 102 | m[r] = s 103 | } 104 | a.SetSymbols(m) 105 | a.scaninit = true 106 | } 107 | return a.Scanner.Scan(line) 108 | } 109 | 110 | func (a *Apl) SetOutput(w io.Writer) { 111 | a.stdout = w 112 | } 113 | 114 | func (a *Apl) GetOutput() io.Writer { 115 | if a.stdout == nil { 116 | return ioutil.Discard 117 | } 118 | return a.stdout 119 | } 120 | 121 | func (a *Apl) SetImage(w ImageWriter) { 122 | a.stdimg = w 123 | } 124 | 125 | func newEnv() *env { 126 | return &env{vars: map[string]Value{}} 127 | } 128 | -------------------------------------------------------------------------------- /apl/array_test.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import "testing" 4 | 5 | func TestIdx(t *testing.T) { 6 | testCases := []struct { 7 | shape []int 8 | idx []int 9 | n int 10 | }{ 11 | {[]int{2}, []int{1}, 1}, 12 | {[]int{2, 2, 4}, []int{1, 1, 3}, 15}, 13 | {[]int{3, 4, 2}, []int{1, 3, 0}, 14}, 14 | } 15 | 16 | // Test IdxConverter 17 | for _, tc := range testCases { 18 | ic, idx := NewIdxConverter(tc.shape) 19 | ic.Indexes(tc.n, idx) 20 | for i := range idx { 21 | if idx[i] != tc.idx[i] { 22 | t.Fatalf("expected %v got %v", tc.idx, idx) 23 | } 24 | } 25 | n := ic.Index(idx) 26 | if n != tc.n { 27 | t.Fatalf("expected %d got %d", tc.n, n) 28 | } 29 | } 30 | 31 | // Test ArraySize and IncArrayIndex 32 | for _, tc := range testCases { 33 | ar := MixedArray{Dims: tc.shape} 34 | ic, idx := NewIdxConverter(tc.shape) 35 | for i := 0; i < ar.Size(); i++ { 36 | n := ic.Index(idx) 37 | if n != i { 38 | t.Fatalf("expected %d got %d", i, n) 39 | } 40 | IncArrayIndex(idx, tc.shape) 41 | } 42 | for i := range idx { 43 | if idx[i] != 0 { 44 | t.Fatalf("idx should be zeros: %v", idx) 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /apl/assign.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Assignment contains the unevaluated left argument of an assignment. 9 | // It may be an identifier or an expression containing an identifier. 10 | // The identifier may be followed by a modifying function. 11 | type assignment struct { 12 | e expr 13 | } 14 | 15 | func (as assignment) String(f Format) string { 16 | return "specification " + as.e.String(f) 17 | } 18 | func (as assignment) Copy() Value { return as } // This does not do a deep copy. 19 | 20 | func (as assignment) Eval(a *Apl) (Value, error) { 21 | return as, nil 22 | } 23 | 24 | // Assignment is the evaluated left part of an assignment. 25 | // It contains the Identifier, it's indexes and a modification function. 26 | type Assignment struct { 27 | Identifier string 28 | Identifiers []string // Multiple identifiers for vector assignment 29 | Indexes Value // Should be convertible to an Index vector 30 | Modifier Function 31 | } 32 | 33 | func (as *Assignment) Copy() Value { 34 | r := Assignment{ 35 | Identifier: as.Identifier, 36 | } 37 | if as.Identifiers != nil { 38 | r.Identifiers = make([]string, len(as.Identifiers)) 39 | copy(r.Identifiers, as.Identifiers) 40 | } 41 | if as.Indexes != nil { 42 | r.Indexes = as.Indexes.Copy() 43 | } 44 | if as.Modifier != nil { 45 | r.Modifier = as.Modifier 46 | } 47 | return &r 48 | } 49 | 50 | func (as *Assignment) String(f Format) string { 51 | s := "" 52 | if as.Indexes != nil { 53 | s = "indexed/selective " 54 | } 55 | if as.Modifier != nil { 56 | s += "modified " 57 | } 58 | id := as.Identifier 59 | if as.Identifiers != nil { 60 | id = strings.Join(as.Identifiers, " ") 61 | } 62 | return "assignment to " + id 63 | } 64 | 65 | // EvalAssign evalutes the left part of an assignment and 66 | // returns it as an Assignment value. 67 | // It handles indexed, selective and modified assignment. 68 | func evalAssign(a *Apl, e expr, modifier Function) (Value, error) { 69 | as := Assignment{ 70 | Modifier: modifier, 71 | } 72 | 73 | // A function assignment can have no selections or modifications. 74 | if f, ok := e.(fnVar); ok { 75 | as.Identifier = string(f) 76 | return &as, nil 77 | } 78 | 79 | // Modified assignment masks the left argument in an assignment expr. 80 | if as, ok := e.(assignment); ok { 81 | e = as.e 82 | } 83 | 84 | // Vector assignment can only contain a vector of numVars. 85 | if ae, ok := e.(array); ok { 86 | as.Identifiers = make([]string, len(ae)) 87 | for i, v := range ae { 88 | if nv, ok := v.(numVar); ok { 89 | as.Identifiers[i] = nv.name 90 | } else { 91 | return nil, fmt.Errorf("vector assignment can contain only numVars: %T", v) 92 | } 93 | } 94 | return &as, nil 95 | } 96 | 97 | // The identifier is the right-most argument in the expression. 98 | // The selection function (if present) is the function left to the Identifier. 99 | selection := false 100 | r := e 101 | search: 102 | for { 103 | switch v := r.(type) { 104 | case numVar: 105 | as.Identifier = v.name 106 | break search 107 | case *function: 108 | r = v.right 109 | v.selection = true 110 | selection = true 111 | default: 112 | return nil, fmt.Errorf("unknown type in assignment expression: %T", r) 113 | } 114 | } 115 | 116 | if selection { 117 | if idx, err := e.Eval(a); err != nil { 118 | return nil, err 119 | } else { 120 | as.Indexes = idx 121 | } 122 | } 123 | 124 | return &as, nil 125 | } 126 | -------------------------------------------------------------------------------- /apl/big/bigfloat/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alberto Donizetti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /apl/big/bigfloat/README: -------------------------------------------------------------------------------- 1 | This is a copy of github.com/ALTree/bigfloat -------------------------------------------------------------------------------- /apl/big/bigfloat/exp.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import ( 4 | "math" 5 | "math/big" 6 | ) 7 | 8 | // Exp returns a big.Float representation of exp(z). Precision is 9 | // the same as the one of the argument. The function returns +Inf 10 | // when z = +Inf, and 0 when z = -Inf. 11 | func Exp(z *big.Float) *big.Float { 12 | 13 | // exp(0) == 1 14 | if z.Sign() == 0 { 15 | return big.NewFloat(1).SetPrec(z.Prec()) 16 | } 17 | 18 | // Exp(+Inf) = +Inf 19 | if z.IsInf() && z.Sign() > 0 { 20 | return big.NewFloat(math.Inf(+1)).SetPrec(z.Prec()) 21 | } 22 | 23 | // Exp(-Inf) = 0 24 | if z.IsInf() && z.Sign() < 0 { 25 | return big.NewFloat(0).SetPrec(z.Prec()) 26 | } 27 | 28 | guess := new(big.Float) 29 | 30 | // try to get initial estimate using IEEE-754 math 31 | zf, _ := z.Float64() 32 | if zfs := math.Exp(zf); zfs == math.Inf(+1) || zfs == 0 { 33 | // too big or too small for IEEE-754 math, 34 | // perform argument reduction using 35 | // e^{2z} = (e^z)² 36 | halfZ := new(big.Float).Mul(z, big.NewFloat(0.5)) 37 | halfExp := Exp(halfZ.SetPrec(z.Prec() + 64)) 38 | return new(big.Float).Mul(halfExp, halfExp).SetPrec(z.Prec()) 39 | } else { 40 | // we got a nice IEEE-754 estimate 41 | guess.SetFloat64(zfs) 42 | } 43 | 44 | // f(t)/f'(t) = t*(log(t) - z) 45 | f := func(t *big.Float) *big.Float { 46 | x := new(big.Float) 47 | x.Sub(Log(t), z) 48 | return x.Mul(x, t) 49 | } 50 | 51 | x := newton(f, guess, z.Prec()) 52 | 53 | return x 54 | } 55 | -------------------------------------------------------------------------------- /apl/big/bigfloat/log.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import ( 4 | "math" 5 | "math/big" 6 | ) 7 | 8 | // Log returns a big.Float representation of the natural logarithm of 9 | // z. Precision is the same as the one of the argument. The function 10 | // panics if z is negative, returns -Inf when z = 0, and +Inf when z = 11 | // +Inf 12 | func Log(z *big.Float) *big.Float { 13 | 14 | // panic on negative z 15 | if z.Sign() == -1 { 16 | panic("Log: argument is negative") 17 | } 18 | 19 | // Log(0) = -Inf 20 | if z.Sign() == 0 { 21 | return big.NewFloat(math.Inf(-1)).SetPrec(z.Prec()) 22 | } 23 | 24 | prec := z.Prec() + 64 // guard digits 25 | 26 | one := big.NewFloat(1).SetPrec(prec) 27 | two := big.NewFloat(2).SetPrec(prec) 28 | four := big.NewFloat(4).SetPrec(prec) 29 | 30 | // Log(1) = 0 31 | if z.Cmp(one) == 0 { 32 | return big.NewFloat(0).SetPrec(z.Prec()) 33 | } 34 | 35 | // Log(+Inf) = +Inf 36 | if z.IsInf() { 37 | return big.NewFloat(math.Inf(+1)).SetPrec(z.Prec()) 38 | } 39 | 40 | x := new(big.Float).SetPrec(prec) 41 | 42 | // if 0 < z < 1 we compute log(z) as -log(1/z) 43 | var neg bool 44 | if z.Cmp(one) < 0 { 45 | x.Quo(one, z) 46 | neg = true 47 | } else { 48 | x.Set(z) 49 | } 50 | 51 | // We scale up x until x >= 2**(prec/2), and then we'll be allowed 52 | // to use the AGM formula for Log(x). 53 | // 54 | // Double x until the condition is met, and keep track of the 55 | // number of doubling we did (needed to scale back later). 56 | 57 | lim := new(big.Float) 58 | lim.SetMantExp(two, int(prec/2)) 59 | 60 | k := 0 61 | for x.Cmp(lim) < 0 { 62 | x.Mul(x, x) 63 | k++ 64 | } 65 | 66 | // Compute the natural log of x using the fact that 67 | // log(x) = π / (2 * AGM(1, 4/x)) 68 | // if 69 | // x >= 2**(prec/2), 70 | // where prec is the desired precision (in bits) 71 | pi := pi(prec) 72 | agm := agm(one, x.Quo(four, x)) // agm = AGM(1, 4/x) 73 | 74 | x.Quo(pi, x.Mul(two, agm)) // reuse x, we don't need it 75 | 76 | if neg { 77 | x.Neg(x) 78 | } 79 | 80 | // scale the result back multiplying by 2**-k 81 | // reuse lim to reduce allocations. 82 | x.Mul(x, lim.SetMantExp(one, -k)) 83 | 84 | return x.SetPrec(z.Prec()) 85 | } 86 | -------------------------------------------------------------------------------- /apl/big/bigfloat/misc.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import "math/big" 4 | 5 | // agm returns the arithmetic-geometric mean of a and b. 6 | // a and b must have the same precision. 7 | func agm(a, b *big.Float) *big.Float { 8 | 9 | if a.Prec() != b.Prec() { 10 | panic("agm: different precisions") 11 | } 12 | 13 | prec := a.Prec() 14 | 15 | // do not overwrite a and b 16 | a2 := new(big.Float).Copy(a).SetPrec(prec + 64) 17 | b2 := new(big.Float).Copy(b).SetPrec(prec + 64) 18 | 19 | if a2.Cmp(b2) == -1 { 20 | a2, b2 = b2, a2 21 | } 22 | // a2 >= b2 23 | 24 | // set lim to 2**(-prec) 25 | lim := new(big.Float) 26 | lim.SetMantExp(big.NewFloat(1).SetPrec(prec+64), -int(prec+1)) 27 | 28 | half := big.NewFloat(0.5) 29 | t := new(big.Float) 30 | 31 | for t.Sub(a2, b2).Cmp(lim) != -1 { 32 | t.Copy(a2) 33 | a2.Add(a2, b2).Mul(a2, half) 34 | b2 = Sqrt(b2.Mul(b2, t)) 35 | } 36 | 37 | return a2.SetPrec(prec) 38 | } 39 | 40 | var piCache *big.Float 41 | var piCachePrec uint 42 | var enablePiCache bool = true 43 | 44 | func init() { 45 | if !enablePiCache { 46 | return 47 | } 48 | 49 | piCache, _, _ = new(big.Float).SetPrec(1024).Parse("3."+ 50 | "14159265358979323846264338327950288419716939937510"+ 51 | "58209749445923078164062862089986280348253421170679"+ 52 | "82148086513282306647093844609550582231725359408128"+ 53 | "48111745028410270193852110555964462294895493038196"+ 54 | "44288109756659334461284756482337867831652712019091"+ 55 | "45648566923460348610454326648213393607260249141273"+ 56 | "72458700660631558817488152092096282925409171536444", 10) 57 | 58 | piCachePrec = 1024 59 | } 60 | 61 | // pi returns pi to prec bits of precision 62 | func pi(prec uint) *big.Float { 63 | 64 | if prec <= piCachePrec && enablePiCache { 65 | return new(big.Float).Copy(piCache).SetPrec(prec) 66 | } 67 | 68 | // Following R. P. Brent, Multiple-precision zero-finding 69 | // methods and the complexity of elementary function evaluation, 70 | // in Analytic Computational Complexity, Academic Press, 71 | // New York, 1975, Section 8. 72 | 73 | half := big.NewFloat(0.5) 74 | two := big.NewFloat(2).SetPrec(prec + 64) 75 | 76 | // initialization 77 | a := big.NewFloat(1).SetPrec(prec + 64) // a = 1 78 | b := new(big.Float).Mul(Sqrt(two), half) // b = 1/√2 79 | t := big.NewFloat(0.25).SetPrec(prec + 64) // t = 1/4 80 | x := big.NewFloat(1).SetPrec(prec + 64) // x = 1 81 | 82 | // limit is 2**(-prec) 83 | lim := new(big.Float) 84 | lim.SetMantExp(big.NewFloat(1).SetPrec(prec+64), -int(prec+1)) 85 | 86 | // temp variables 87 | y := new(big.Float) 88 | for y.Sub(a, b).Cmp(lim) != -1 { // assume a > b 89 | y.Copy(a) 90 | a.Add(a, b).Mul(a, half) // a = (a+b)/2 91 | b = Sqrt(b.Mul(b, y)) // b = √(ab) 92 | 93 | y.Sub(a, y) // y = a - y 94 | y.Mul(y, y).Mul(y, x) // y = x(a-y)² 95 | t.Sub(t, y) // t = t - x(a-y)² 96 | x.Mul(x, two) // x = 2x 97 | } 98 | 99 | a.Mul(a, a).Quo(a, t) // π = a² / t 100 | a.SetPrec(prec) 101 | 102 | if enablePiCache { 103 | piCache.Copy(a) 104 | piCachePrec = prec 105 | } 106 | 107 | return a 108 | } 109 | 110 | // returns an approximate (to precision dPrec) solution to 111 | // f(t) = 0 112 | // using the Newton Method. 113 | // fOverDf needs to be a fuction returning f(t)/f'(t). 114 | // t must not be changed by fOverDf. 115 | // guess is the initial guess (and it's not preserved). 116 | func newton(fOverDf func(z *big.Float) *big.Float, guess *big.Float, dPrec uint) *big.Float { 117 | 118 | prec, guard := guess.Prec(), uint(64) 119 | guess.SetPrec(prec + guard) 120 | 121 | for prec < 2*dPrec { 122 | guess.Sub(guess, fOverDf(guess)) 123 | prec *= 2 124 | guess.SetPrec(prec + guard) 125 | } 126 | 127 | return guess.SetPrec(dPrec) 128 | } 129 | -------------------------------------------------------------------------------- /apl/big/bigfloat/pow.go: -------------------------------------------------------------------------------- 1 | package bigfloat 2 | 3 | import "math/big" 4 | 5 | // Pow returns a big.Float representation of z**w. Precision is the same as the one 6 | // of the first argument. The function panics when z is negative. 7 | func Pow(z *big.Float, w *big.Float) *big.Float { 8 | 9 | if z.Sign() < 0 { 10 | panic("Pow: negative base") 11 | } 12 | 13 | // Pow(z, 0) = 1.0 14 | if w.Sign() == 0 { 15 | return big.NewFloat(1).SetPrec(z.Prec()) 16 | } 17 | 18 | // Pow(z, 1) = z 19 | // Pow(+Inf, n) = +Inf 20 | if w.Cmp(big.NewFloat(1)) == 0 || z.IsInf() { 21 | return new(big.Float).Copy(z) 22 | } 23 | 24 | // Pow(z, -w) = 1 / Pow(z, w) 25 | if w.Sign() < 0 { 26 | x := new(big.Float) 27 | zExt := new(big.Float).Copy(z).SetPrec(z.Prec() + 64) 28 | wNeg := new(big.Float).Neg(w) 29 | return x.Quo(big.NewFloat(1), Pow(zExt, wNeg)).SetPrec(z.Prec()) 30 | } 31 | 32 | // w integer fast path (disabled because introduces rounding 33 | // errors) 34 | if false && w.IsInt() { 35 | wi, _ := w.Int64() 36 | return powInt(z, int(wi)) 37 | } 38 | 39 | // compute w**z as exp(z log(w)) 40 | x := new(big.Float).SetPrec(z.Prec() + 64) 41 | logZ := Log(new(big.Float).Copy(z).SetPrec(z.Prec() + 64)) 42 | x.Mul(w, logZ) 43 | x = Exp(x) 44 | return x.SetPrec(z.Prec()) 45 | 46 | } 47 | 48 | // fast path for z**w when w is an integer 49 | func powInt(z *big.Float, w int) *big.Float { 50 | 51 | // get mantissa and exponent of z 52 | mant := new(big.Float) 53 | exp := z.MantExp(mant) 54 | 55 | // result's exponent 56 | exp = exp * w 57 | 58 | // result's mantissa 59 | x := big.NewFloat(1).SetPrec(z.Prec()) 60 | 61 | // Classic right-to-left binary exponentiation 62 | for w > 0 { 63 | if w%2 == 1 { 64 | x.Mul(x, mant) 65 | } 66 | w >>= 1 67 | mant.Mul(mant, mant) 68 | } 69 | 70 | return new(big.Float).SetMantExp(x, exp) 71 | } 72 | -------------------------------------------------------------------------------- /apl/big/bigfloat/sqrt.go: -------------------------------------------------------------------------------- 1 | // Package bigfloat provides the implementation of a few additional operations for the 2 | // standard library big.Float type. 3 | package bigfloat 4 | 5 | import ( 6 | "math" 7 | "math/big" 8 | ) 9 | 10 | // Sqrt returns a big.Float representation of the square root of 11 | // z. Precision is the same as the one of the argument. The function 12 | // panics if z is negative, returns ±0 when z = ±0, and +Inf when z = 13 | // +Inf. 14 | func Sqrt(z *big.Float) *big.Float { 15 | 16 | // panic on negative z 17 | if z.Sign() == -1 { 18 | panic("Sqrt: argument is negative") 19 | } 20 | 21 | // √±0 = ±0 22 | if z.Sign() == 0 { 23 | return big.NewFloat(float64(z.Sign())) 24 | } 25 | 26 | // √+Inf = +Inf 27 | if z.IsInf() { 28 | return big.NewFloat(math.Inf(+1)) 29 | } 30 | 31 | // Compute √(a·2**b) as 32 | // √(a)·2**b/2 if b is even 33 | // √(2a)·2**b/2 if b > 0 is odd 34 | // √(0.5a)·2**b/2 if b < 0 is odd 35 | // 36 | // The difference in the odd exponent case is due to the fact that 37 | // exp/2 is rounded in different directions when exp is negative. 38 | mant := new(big.Float) 39 | exp := z.MantExp(mant) 40 | switch exp % 2 { 41 | case 1: 42 | mant.Mul(big.NewFloat(2), mant) 43 | case -1: 44 | mant.Mul(big.NewFloat(0.5), mant) 45 | } 46 | 47 | // Solving x² - z = 0 directly requires a Quo call, but it's 48 | // faster for small precisions. 49 | // 50 | // Solving 1/x² - z = 0 avoids the Quo call and is much faster for 51 | // high precisions. 52 | // 53 | // Use sqrtDirect for prec <= 128 and sqrtInverse for prec > 128. 54 | var x *big.Float 55 | if z.Prec() <= 128 { 56 | x = sqrtDirect(mant) 57 | } else { 58 | x = sqrtInverse(mant) 59 | } 60 | 61 | // re-attach the exponent and return 62 | return x.SetMantExp(x, exp/2) 63 | 64 | } 65 | 66 | // compute √z using newton to solve 67 | // t² - z = 0 for t 68 | func sqrtDirect(z *big.Float) *big.Float { 69 | // f(t)/f'(t) = 0.5(t² - z)/t 70 | half := big.NewFloat(0.5) 71 | f := func(t *big.Float) *big.Float { 72 | x := new(big.Float).Mul(t, t) // x = t² 73 | x.Sub(x, z) // x = t² - z 74 | x.Mul(half, x) // x = 0.5(t² - z) 75 | return x.Quo(x, t) // return x = 0.5(t² - z)/t 76 | } 77 | 78 | // initial guess 79 | zf, _ := z.Float64() 80 | guess := big.NewFloat(math.Sqrt(zf)) 81 | 82 | return newton(f, guess, z.Prec()) 83 | } 84 | 85 | // compute √z using newton to solve 86 | // 1/t² - z = 0 for x and then inverting. 87 | func sqrtInverse(z *big.Float) *big.Float { 88 | // f(t)/f'(t) = -0.5t(1 - zt²) 89 | nhalf := big.NewFloat(-0.5) 90 | one := big.NewFloat(1) 91 | f := func(t *big.Float) *big.Float { 92 | u := new(big.Float) 93 | u.Mul(t, t) // u = t² 94 | u.Mul(u, z) // u = zt² 95 | u.Sub(one, u) // u = 1 - zt² 96 | u.Mul(u, nhalf) // u = -0.5(1 - zt²) 97 | return new(big.Float).Mul(t, u) // x = -0.5t(1 - zt²) 98 | } 99 | 100 | // initial guess 101 | zf, _ := z.Float64() 102 | guess := big.NewFloat(1 / math.Sqrt(zf)) 103 | 104 | // There's another operation after newton, 105 | // so we need to force it to return at least 106 | // a few guard digits. Use 32. 107 | x := newton(f, guess, z.Prec()+32) 108 | return x.Mul(z, x).SetPrec(z.Prec()) 109 | } 110 | -------------------------------------------------------------------------------- /apl/big/register.go: -------------------------------------------------------------------------------- 1 | package big 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "reflect" 7 | 8 | "github.com/ktye/iv/apl" 9 | "github.com/ktye/iv/apl/numbers" 10 | ) 11 | 12 | func Register(a *apl.Apl, name string) { 13 | pkg := map[string]apl.Value{ 14 | "set": apl.ToFunction(settower), 15 | } 16 | if name == "" { 17 | name = "big" 18 | } 19 | a.RegisterPackage(name, pkg) 20 | } 21 | 22 | // SetBigTower sets the numerical tower to Int->Rat. 23 | func SetBigTower(a *apl.Apl) { 24 | m := make(map[reflect.Type]*apl.Numeric) 25 | m[reflect.TypeOf(Int{})] = &apl.Numeric{ 26 | Class: 0, 27 | Parse: ParseInt, 28 | Uptype: intToRat, 29 | } 30 | m[reflect.TypeOf(Rat{})] = &apl.Numeric{ 31 | Class: 1, 32 | Parse: ParseRat, 33 | Uptype: func(n apl.Number) (apl.Number, bool) { return n, false }, 34 | } 35 | t := apl.Tower{ 36 | Numbers: m, 37 | Import: func(n apl.Number) apl.Number { 38 | if b, ok := n.(apl.Bool); ok { 39 | if b { 40 | return Int{big.NewInt(1)} 41 | } 42 | return Int{big.NewInt(0)} 43 | } else if n, ok := n.(apl.Int); ok { 44 | return Int{big.NewInt(int64(n))} 45 | } 46 | return n 47 | }, 48 | Uniform: func(v []apl.Value) (apl.Value, bool) { return nil, false }, 49 | } 50 | if err := a.SetTower(t); err != nil { 51 | panic(err) 52 | } 53 | } 54 | 55 | // SetPreciseTower sets the numerical tower to Float->Complex with the given precision. 56 | func SetPreciseTower(a *apl.Apl, prec uint) { 57 | m := make(map[reflect.Type]*apl.Numeric) 58 | m[reflect.TypeOf(Float{})] = &apl.Numeric{ 59 | Class: 0, 60 | Parse: func(s string) (apl.Number, bool) { return ParseFloat(s, prec) }, 61 | Uptype: floatToComplex, 62 | } 63 | m[reflect.TypeOf(Complex{})] = &apl.Numeric{ 64 | Class: 1, 65 | Parse: func(s string) (apl.Number, bool) { return ParseComplex(s, prec) }, 66 | Uptype: func(n apl.Number) (apl.Number, bool) { return n, false }, 67 | } 68 | t := apl.Tower{ 69 | Numbers: m, 70 | Import: func(n apl.Number) apl.Number { 71 | if b, ok := n.(apl.Bool); ok { 72 | if b { 73 | return Float{big.NewFloat(1).SetPrec(prec)} 74 | } 75 | return Float{big.NewFloat(0).SetPrec(prec)} 76 | } else if n, ok := n.(apl.Int); ok { 77 | f := Float{big.NewFloat(float64(n)).SetPrec(prec)} 78 | return f 79 | } 80 | return n 81 | }, 82 | Uniform: func(v []apl.Value) (apl.Value, bool) { return nil, false }, 83 | } 84 | if err := a.SetTower(t); err != nil { 85 | panic(err) 86 | } 87 | } 88 | 89 | func settower(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 90 | n, ok := R.(apl.Number) 91 | if ok == false { 92 | return nil, fmt.Errorf("set needs a number (0, 1, 256...)") 93 | } 94 | idx, ok := n.ToIndex() 95 | if ok == false { 96 | return nil, fmt.Errorf("set needs an integer") 97 | } 98 | if idx < 0 { 99 | return nil, fmt.Errorf("set: precision must be positive") 100 | } else if idx == 0 { 101 | numbers.Register(a) 102 | } else if idx == 1 { 103 | SetBigTower(a) 104 | } else { 105 | SetPreciseTower(a, uint(idx)) 106 | } 107 | return R, nil 108 | } 109 | 110 | func getformat(f apl.Format, num apl.Value) (string, bool) { 111 | if f.Fmt == nil { 112 | return "", false 113 | } 114 | s := f.Fmt[reflect.TypeOf(num)] 115 | if len(s) > 0 && s[0] == '-' { 116 | return s[1:], true 117 | } 118 | return s, false 119 | } 120 | -------------------------------------------------------------------------------- /apl/bool.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Bool bool 8 | 9 | func (b Bool) String(f Format) string { 10 | if f.PP < 0 { 11 | if b { 12 | return "1b" 13 | } 14 | return "0b" 15 | } 16 | if b { 17 | return "1" 18 | } 19 | return "0" 20 | } 21 | func (b Bool) Copy() Value { return b } 22 | 23 | func (i Bool) Less(v Value) (Bool, bool) { 24 | j, ok := v.(Bool) 25 | if ok == false { 26 | return false, false 27 | } 28 | return i == false && j == true, true 29 | } 30 | 31 | func (i Bool) ToIndex() (int, bool) { 32 | if i { 33 | return 1, true 34 | } 35 | return 0, true 36 | } 37 | 38 | // BoolArray is a uniform array of type bool. 39 | type BoolArray struct { 40 | Dims []int 41 | Bools []bool 42 | } 43 | 44 | func (b BoolArray) String(f Format) string { 45 | return ArrayString(f, b) 46 | } 47 | 48 | func (b BoolArray) Copy() Value { 49 | r := BoolArray{Dims: CopyShape(b), Bools: make([]bool, len(b.Bools))} 50 | copy(r.Bools, b.Bools) 51 | return r 52 | } 53 | 54 | func (b BoolArray) At(i int) Value { 55 | return Bool(b.Bools[i]) 56 | } 57 | 58 | func (b BoolArray) Shape() []int { 59 | return b.Dims 60 | } 61 | 62 | func (b BoolArray) Size() int { 63 | return len(b.Bools) 64 | } 65 | 66 | func (b BoolArray) Zero() Value { 67 | return Bool(false) 68 | } 69 | 70 | func (b BoolArray) Set(i int, v Value) error { 71 | if i < 0 || i > len(b.Bools) { 72 | return fmt.Errorf("index out of range") 73 | } 74 | if c, ok := v.(Bool); ok { 75 | b.Bools[i] = bool(c) 76 | return nil 77 | } 78 | return fmt.Errorf("cannot assign %T to BoolArray", v) 79 | } 80 | 81 | func (s BoolArray) Make(shape []int) Uniform { 82 | return BoolArray{ 83 | Dims: shape, 84 | Bools: make([]bool, Prod(shape)), 85 | } 86 | } 87 | 88 | func makeBoolArray(v []Value) BoolArray { 89 | b := make([]bool, len(v)) 90 | for i, e := range v { 91 | b[i] = bool(e.(Bool)) 92 | } 93 | return BoolArray{ 94 | Dims: []int{len(v)}, 95 | Bools: b, 96 | } 97 | } 98 | 99 | func (b BoolArray) Reshape(shape []int) Value { 100 | res := BoolArray{ 101 | Dims: shape, 102 | Bools: make([]bool, Prod(shape)), 103 | } 104 | k := 0 105 | for i := range res.Bools { 106 | res.Bools[i] = b.Bools[k] 107 | k++ 108 | if k == len(b.Bools) { 109 | k = 0 110 | } 111 | } 112 | return res 113 | } 114 | -------------------------------------------------------------------------------- /apl/domain.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | // Domain represents the application domain of a function or operator. 4 | // Calling To converts the left and right input arguments and returns true, 5 | // if the types or values are compatible with the function or operator. 6 | // Otherwise they return false. 7 | // If To returns false, it must return the original input values. 8 | // This is important for a Not combination to work properly. 9 | // String is used in documentation for a concise type/value description. 10 | // 11 | // Standard Domain implementations and universal combination functions 12 | // are in the domain package. 13 | type Domain interface { 14 | To(*Apl, Value, Value) (Value, Value, bool) 15 | String(Format) string 16 | } 17 | -------------------------------------------------------------------------------- /apl/domain/channel.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/ktye/iv/apl" 4 | 5 | // IsChannel tests if the value is a channel. 6 | func IsChannel(child SingleDomain) SingleDomain { 7 | return channel{child} 8 | } 9 | 10 | type channel struct { 11 | child SingleDomain 12 | } 13 | 14 | func (c channel) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 15 | if _, ok := V.(apl.Channel); ok { 16 | return propagate(a, V, c.child) 17 | } 18 | return V, false 19 | } 20 | 21 | func (c channel) String(f apl.Format) string { 22 | name := "channel" 23 | if c.child == nil { 24 | return name 25 | } 26 | return name + " " + c.child.String(f) 27 | } 28 | -------------------------------------------------------------------------------- /apl/domain/compare.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | /* TODO move to numbers package 4 | import ( 5 | "errors" 6 | "fmt" 7 | "math" 8 | "math/cmplx" 9 | "reflect" 10 | 11 | "github.com/ktye/iv/apl" 12 | ) 13 | 14 | var ErrNaN error 15 | 16 | type ErrInf int 17 | 18 | func (e ErrInf) Error() string { 19 | if e < 0 { 20 | return "-Inf" 21 | } else { 22 | return "Inf" 23 | } 24 | } 25 | 26 | func init() { 27 | ErrNaN = errors.New("NaN") 28 | } 29 | 30 | // IsFloatErr returns ErrInf(1), ErrInf(-1) or ErrNaN, if the 31 | // Float or Complex argument is not finite. 32 | func IsFloatErr(f apl.Value) error { 33 | switch t := f.(type) { 34 | case apl.Float: 35 | v := float64(t) 36 | if math.IsInf(v, 1) { 37 | return ErrInf(1) 38 | } else if math.IsInf(v, -1) { 39 | return ErrInf(-1) 40 | } else if math.IsNaN(v) { 41 | return ErrNaN 42 | } 43 | return nil 44 | case apl.Complex: 45 | c := complex128(t) 46 | if cmplx.IsInf(c) { 47 | return ErrInf(1) 48 | } else if cmplx.IsNaN(c) { 49 | return ErrNaN 50 | } 51 | return nil 52 | default: 53 | return fmt.Errorf("IsFloatErr expected Float or Complex: %T", f) 54 | } 55 | } 56 | 57 | // CompareScalars compares and b and returns 58 | // a == b, a < b. 59 | // Non-numeric values are checked for equality. 60 | // If they have the same type and are a Lesser, they 61 | // are also checke for a < b. 62 | // Numeric types are converted to the same type before comparison. 63 | func CompareScalars(a, b apl.Value) (bool, bool, error) { 64 | // Equal types and equal. 65 | if a == b { 66 | return true, false, nil 67 | } 68 | // EqualTypes have lesser. 69 | if reflect.TypeOf(a) == reflect.TypeOf(b) { 70 | if al, ok := a.(Lesser); ok { 71 | le := al.Less(b) 72 | return false, le, nil 73 | } 74 | } 75 | // Convert both to numbers. 76 | num := number{nil, true} 77 | an, aok := num.To(nil, a) 78 | bn, bok := num.To(nil, b) 79 | if aok == false || bok == false { 80 | return false, false, fmt.Errorf("cannot compare %T with %T", a, b) 81 | } 82 | 83 | an, bn = MustPromote(an, bn) 84 | switch an := an.(type) { 85 | case apl.Bool: 86 | if an == bn.(apl.Bool) { 87 | return true, false, nil 88 | } else if bool(an) == false && bool(bn.(apl.Bool)) == true { 89 | return false, true, nil 90 | } 91 | return false, false, nil 92 | case apl.Int: 93 | i := bn.(apl.Int) 94 | return an == i, an < i, nil 95 | case apl.Float: 96 | f := bn.(apl.Float) 97 | if math.IsNaN(float64(f)) || math.IsNaN(float64(an)) { 98 | return false, false, ErrNaN 99 | } 100 | return an == bn, an < f, nil 101 | case apl.Complex: 102 | c := bn.(apl.Complex) 103 | if cmplx.IsNaN(complex128(an)) || cmplx.IsNaN(complex128(c)) { 104 | return false, false, ErrNaN 105 | } 106 | if an == c { 107 | return true, false, nil 108 | } 109 | if imag(an) != 0 || imag(c) != 0 { 110 | return false, false, fmt.Errorf("cannot compare complex numbers") 111 | } 112 | return real(an) == real(c), real(an) < real(c), nil 113 | } 114 | return false, false, fmt.Errorf("cannot compare %T with %T", a, b) 115 | } 116 | 117 | // Lesser is used for scalar comparison. 118 | // Custom types may implement it. 119 | // It must be called only after checking that the types are identical. 120 | // It returns true, if the receiver is less that b. 121 | type Lesser interface { 122 | Less(b interface{}) bool 123 | } 124 | 125 | // MustPromote returns two numbers of the same type, by possibly uptyping one of them. 126 | // mustPromote can only be called with input types apl.Bool, apl.Int, apl.Float and apl.Complex. 127 | func MustPromote(L, R apl.Value) (apl.Value, apl.Value) { 128 | to := ToBool(nil) 129 | if _, ok := L.(apl.Complex); ok { 130 | to = ToComplex(nil) 131 | } else if _, ok := R.(apl.Complex); ok { 132 | to = ToComplex(nil) 133 | } else if _, ok := L.(apl.Float); ok { 134 | to = ToFloat(nil) 135 | } else if _, ok := R.(apl.Float); ok { 136 | to = ToFloat(nil) 137 | } else if _, ok := L.(apl.Int); ok { 138 | to = ToInt(nil) 139 | } else if _, ok := R.(apl.Int); ok { 140 | to = ToInt(nil) 141 | } 142 | L, _ = to.To(nil, L) 143 | R, _ = to.To(nil, R) 144 | return L, R 145 | } 146 | */ 147 | -------------------------------------------------------------------------------- /apl/domain/functions.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "github.com/ktye/iv/apl" 5 | ) 6 | 7 | func Function(child SingleDomain) SingleDomain { 8 | return function{child} 9 | } 10 | 11 | type function struct{ child SingleDomain } 12 | 13 | func (f function) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 14 | if _, ok := V.(apl.Function); ok == false { 15 | return V, false 16 | } 17 | if f.child == nil { 18 | return V, true 19 | } 20 | if v, ok := f.child.To(a, V); ok { 21 | return v, true 22 | } 23 | return V, false 24 | } 25 | func (f function) String(af apl.Format) string { 26 | if f.child == nil { 27 | return "function" 28 | } 29 | return "function " + f.child.String(af) 30 | } 31 | 32 | func IsPrimitive(p string) SingleDomain { 33 | return primitive(p) 34 | } 35 | 36 | type primitive string 37 | 38 | func (p primitive) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 39 | if pf, ok := V.(apl.Primitive); ok && string(pf) == string(p) { 40 | return V, true 41 | } 42 | return V, false 43 | } 44 | func (p primitive) String(f apl.Format) string { 45 | return string(p) 46 | } 47 | -------------------------------------------------------------------------------- /apl/domain/identifier.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/ktye/iv/apl" 4 | 5 | func IsIdentifier(child SingleDomain) SingleDomain { 6 | return identifier{child} 7 | } 8 | 9 | type identifier struct{ child SingleDomain } 10 | 11 | func (id identifier) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 12 | _, ok := V.(apl.Identifier) 13 | if ok == false { 14 | return V, false 15 | } 16 | return propagate(a, V, id.child) 17 | } 18 | 19 | func (id identifier) String(f apl.Format) string { 20 | name := "identfier" 21 | if id.child == nil { 22 | return name 23 | } 24 | return name + " " + id.child.String(f) 25 | } 26 | -------------------------------------------------------------------------------- /apl/domain/image.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | img "image" 5 | "image/color" 6 | 7 | "github.com/ktye/iv/apl" 8 | ) 9 | 10 | // ToImage can convert an int array to an image. 11 | // It also accepts a list with two int arrags: the image data as indexes into a palette, and the palette itself. 12 | func ToImage(child SingleDomain) SingleDomain { 13 | return image{child, true} 14 | } 15 | 16 | func IsImage(child SingleDomain) SingleDomain { 17 | return image{child, false} 18 | } 19 | 20 | type image struct { 21 | child SingleDomain 22 | conv bool 23 | } 24 | 25 | func (i image) String(f apl.Format) string { 26 | name := "image" 27 | if i.conv { 28 | name = "toimage" 29 | } 30 | if i.child == nil { 31 | return name 32 | } 33 | return name + " " + i.child.String(f) 34 | } 35 | 36 | func (im image) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 37 | _, ok := V.(apl.Image) 38 | if im.conv == false && ok == false { 39 | return V, false 40 | } else if im.conv == false && ok { 41 | return propagate(a, V, im.child) 42 | } else if ok { 43 | return propagate(a, V, im.child) 44 | } 45 | 46 | var pal []int 47 | ia, ok := indexarray{nil, true}.To(a, V) 48 | if ok == false { 49 | if l, ok := V.(apl.List); ok == false || len(l) != 2 { 50 | return V, false 51 | } else { 52 | idx, ok := indexarray{nil, true}.To(a, l[0]) 53 | pa, pok := indexarray{nil, true}.To(a, l[1]) 54 | if ok == false || pok == false { 55 | return V, false 56 | } 57 | ia = idx 58 | pal = pa.(apl.IntArray).Ints 59 | } 60 | return V, false 61 | } 62 | 63 | if m, ok := ints2image(ia.(apl.IntArray), pal); ok { 64 | return propagate(a, m, im.child) 65 | } else { 66 | return V, false 67 | } 68 | } 69 | 70 | func ints2image(ia apl.IntArray, pal []int) (apl.Image, bool) { 71 | res := apl.Image{} 72 | shape := ia.Shape() 73 | if len(shape) != 2 { 74 | return res, false 75 | } 76 | 77 | p := make(color.Palette, len(pal)) 78 | for i, n := range pal { 79 | p[i] = toColor(n) 80 | } 81 | 82 | var r img.Rectangle 83 | r.Max.X = shape[1] 84 | r.Max.Y = shape[0] 85 | 86 | var pm *img.Paletted 87 | var im *img.RGBA 88 | if pal == nil { 89 | im = img.NewRGBA(r) 90 | } else { 91 | pm = img.NewPaletted(r, p) 92 | } 93 | i := 0 94 | for y := 0; y < r.Max.Y; y++ { 95 | for x := 0; x < r.Max.X; x++ { 96 | c := ia.Ints[i] 97 | if pal == nil { 98 | im.Set(x, y, toColor(c)) 99 | } else if c >= len(pal) { 100 | pm.SetColorIndex(x, y, 0) 101 | } else { 102 | pm.SetColorIndex(x, y, uint8(c)) 103 | } 104 | i++ 105 | } 106 | } 107 | if pal == nil { 108 | res.Image = im 109 | } else { 110 | res.Image = pm 111 | } 112 | res.Dims = apl.CopyShape(ia) 113 | return res, true 114 | } 115 | 116 | // toColor is copied from apl/image.go 117 | func toColor(i int) color.Color { 118 | u := uint32(i) 119 | return color.RGBA{uint8(u & 0xFF0000 >> 16), uint8(u & 0xFF00 >> 8), uint8(u & 0xFF), ^uint8(u >> 24)} 120 | } 121 | -------------------------------------------------------------------------------- /apl/domain/list.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/ktye/iv/apl" 4 | 5 | // IsList tests if the value is a list. 6 | func IsList(child SingleDomain) SingleDomain { 7 | return list{child, false} 8 | } 9 | 10 | func ToList(child SingleDomain) SingleDomain { 11 | return list{child, true} 12 | } 13 | 14 | type list struct { 15 | child SingleDomain 16 | conv bool 17 | } 18 | 19 | func (v list) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 20 | _, ok := V.(apl.List) 21 | if v.conv == false && ok == false { 22 | return V, false 23 | } else if ok == true { 24 | return propagate(a, V, v.child) 25 | } 26 | 27 | // enlist 28 | return propagate(a, apl.List{V}, v.child) 29 | } 30 | func (v list) String(f apl.Format) string { 31 | name := "list" 32 | if v.conv { 33 | name = "tolist" 34 | } 35 | if v.child == nil { 36 | return name 37 | } 38 | return name + " " + v.child.String(f) 39 | } 40 | -------------------------------------------------------------------------------- /apl/domain/numeric.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/ktye/iv/apl" 4 | 5 | // ToNumber accepts scalars and single size arrays. 6 | // and converts them to scalars if they contain one of the types: 7 | // apl.Bool, apl.Int, apl.Float or apl.Complex. 8 | func ToNumber(child SingleDomain) SingleDomain { 9 | return number{child, true} 10 | } 11 | 12 | // IsNumber accepts scalars if they contain of of the types: 13 | // apl.Bool, apl.Int, apl.Float or apl.Complex 14 | func IsNumber(child SingleDomain) SingleDomain { 15 | return number{child, false} 16 | } 17 | 18 | type number struct { 19 | child SingleDomain 20 | convert bool 21 | } 22 | 23 | func (n number) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 24 | if _, ok := V.(apl.Number); ok { 25 | return propagate(a, V, n.child) 26 | } 27 | if n.convert == false { 28 | return V, false 29 | } 30 | if ar, ok := V.(apl.Array); ok { 31 | if n.convert == false { 32 | return V, false 33 | } 34 | if n := ar.Size(); n != 1 { 35 | return V, false 36 | } 37 | v := ar.At(0) 38 | if _, ok := v.(apl.Number); ok { 39 | return propagate(a, v, n.child) 40 | } 41 | } 42 | return V, false 43 | } 44 | func (n number) String(f apl.Format) string { 45 | name := "number" 46 | if n.convert { 47 | name = "tonumber" 48 | } 49 | if n.child == nil { 50 | return name 51 | } 52 | return name + " " + n.child.String(f) 53 | } 54 | 55 | // ToIndex converts the scalar to an Index. 56 | func ToIndex(child SingleDomain) SingleDomain { 57 | return index{child} 58 | } 59 | 60 | type index struct { 61 | child SingleDomain 62 | } 63 | 64 | func (idx index) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 65 | if b, ok := V.(apl.Bool); ok { 66 | if b == true { 67 | return apl.Int(1), true 68 | } 69 | return apl.Int(0), true 70 | } 71 | if n, ok := V.(apl.Int); ok { 72 | return n, true 73 | } 74 | if n, ok := V.(apl.Number); ok == false { 75 | return V, false 76 | } else { 77 | if i, ok := n.ToIndex(); ok == false { 78 | return V, false 79 | } else { 80 | return propagate(a, apl.Int(i), idx.child) 81 | } 82 | } 83 | } 84 | func (idx index) String(f apl.Format) string { 85 | if idx.child == nil { 86 | return "index" 87 | } else { 88 | return "index " + idx.child.String(f) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /apl/domain/object.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/ktye/iv/apl" 4 | 5 | // IsObject accepts objects 6 | func IsObject(child SingleDomain) SingleDomain { 7 | return objtype{child} 8 | } 9 | 10 | type objtype struct{ child SingleDomain } 11 | 12 | func (s objtype) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 13 | if _, ok := V.(apl.Object); ok { 14 | return propagate(a, V, s.child) 15 | } 16 | return V, false 17 | } 18 | func (s objtype) String(f apl.Format) string { 19 | if s.child == nil { 20 | return "object" 21 | } 22 | return "object" + " " + s.child.String(f) 23 | } 24 | 25 | // IsTable accepts objects 26 | func IsTable(child SingleDomain) SingleDomain { 27 | return table{child} 28 | } 29 | 30 | type table struct{ child SingleDomain } 31 | 32 | func (s table) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 33 | if _, ok := V.(apl.Table); ok { 34 | return propagate(a, V, s.child) 35 | } 36 | return V, false 37 | } 38 | func (s table) String(f apl.Format) string { 39 | if s.child == nil { 40 | return "table" 41 | } 42 | return "table" + " " + s.child.String(f) 43 | } 44 | -------------------------------------------------------------------------------- /apl/domain/operator.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/ktye/iv/apl" 4 | 5 | // MonadicOp is used to define a monadic operator. 6 | func MonadicOp(l SingleDomain) apl.Domain { 7 | return monop{l} 8 | } 9 | 10 | type monop struct { 11 | left SingleDomain 12 | } 13 | 14 | func (m monop) To(a *apl.Apl, L, R apl.Value) (apl.Value, apl.Value, bool) { 15 | if m.left == nil { 16 | return L, R, true 17 | } 18 | if v, ok := m.left.To(a, L); ok { 19 | return v, R, true 20 | } 21 | return L, R, false 22 | } 23 | func (m monop) DyadicOp() bool { return false } 24 | func (m monop) String(f apl.Format) string { 25 | if m.left == nil { 26 | return "LO any" 27 | } 28 | return "LO " + m.left.String(f) 29 | } 30 | 31 | // DyadicOp is used to define a dyadic operator. 32 | func DyadicOp(child apl.Domain) apl.Domain { 33 | return dyop{child} 34 | } 35 | 36 | type dyop struct { 37 | child apl.Domain 38 | } 39 | 40 | func (d dyop) To(a *apl.Apl, L, R apl.Value) (apl.Value, apl.Value, bool) { 41 | if L == nil { 42 | return L, R, false 43 | } 44 | if d.child == nil { 45 | return L, R, true 46 | } 47 | return d.child.To(a, L, R) 48 | } 49 | func (d dyop) DyadicOp() bool { return true } 50 | func (d dyop) String(f apl.Format) string { 51 | if d.child == nil { 52 | return "any" 53 | } 54 | return d.child.String(f) 55 | } 56 | -------------------------------------------------------------------------------- /apl/domain/scalar.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import "github.com/ktye/iv/apl" 4 | 5 | // ToScalar accepts scalars and converts single element arrays to scalars. 6 | func ToScalar(child SingleDomain) SingleDomain { 7 | return scalar{child, true} 8 | } 9 | 10 | // IsScalar accepts values that are not arrays. 11 | func IsScalar(child SingleDomain) SingleDomain { 12 | return scalar{child, false} 13 | } 14 | 15 | type scalar struct { 16 | child SingleDomain 17 | convert bool 18 | } 19 | 20 | func (s scalar) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 21 | v := V 22 | if ar, ok := V.(apl.Array); ok { 23 | if s.convert == false { 24 | return V, false 25 | } 26 | if n := ar.Size(); n != 1 { 27 | return V, false 28 | } 29 | v = ar.At(0) 30 | } 31 | return propagate(a, v, s.child) 32 | } 33 | func (s scalar) String(f apl.Format) string { 34 | name := "scalar" 35 | if s.convert { 36 | name = "toscalar" 37 | } 38 | if s.child == nil { 39 | return name 40 | } 41 | return name + " " + s.child.String(f) 42 | } 43 | -------------------------------------------------------------------------------- /apl/domain/string.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "github.com/ktye/iv/apl" 5 | ) 6 | 7 | // IsString accepts strings 8 | func IsString(child SingleDomain) SingleDomain { 9 | return stringtype{child} 10 | } 11 | 12 | type stringtype struct{ child SingleDomain } 13 | 14 | func (s stringtype) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 15 | if v, ok := V.(apl.String); ok { 16 | if s.child == nil { 17 | return v, true 18 | } 19 | return s.child.To(a, v) 20 | } 21 | return V, false 22 | } 23 | func (s stringtype) String(f apl.Format) string { 24 | if s.child == nil { 25 | return "string" 26 | } 27 | return "string" + " " + s.child.String(f) 28 | } 29 | 30 | // IsStringArray accepts uniform.Strings 31 | func IsStringArray(child SingleDomain) SingleDomain { 32 | return stringstype{child, false} 33 | } 34 | 35 | func ToStringArray(child SingleDomain) SingleDomain { 36 | return stringstype{child, true} 37 | } 38 | 39 | type stringstype struct { 40 | child SingleDomain 41 | convert bool 42 | } 43 | 44 | func (s stringstype) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 45 | if _, ok := V.(apl.StringArray); ok { 46 | return propagate(a, V, s.child) 47 | } else { 48 | if s.convert == false { 49 | return V, false 50 | } 51 | if str, ok := V.(apl.String); ok { 52 | return propagate(a, apl.StringArray{ 53 | Dims: []int{1}, 54 | Strings: []string{string(str)}, 55 | }, s.child) 56 | 57 | } else if ar, ok := V.(apl.Array); ok { 58 | str := make([]string, ar.Size()) 59 | for i := range str { 60 | if sv, ok := ar.At(i).(apl.String); ok { 61 | str[i] = string(sv) 62 | } else { 63 | return V, false 64 | } 65 | } 66 | return propagate(a, apl.StringArray{ 67 | Dims: apl.CopyShape(ar), 68 | Strings: str, 69 | }, s.child) 70 | } else { 71 | return V, false 72 | } 73 | } 74 | } 75 | func (s stringstype) String(f apl.Format) string { 76 | name := "string array" 77 | if s.convert { 78 | name = "to string array" 79 | } 80 | if s.child == nil { 81 | return name 82 | } 83 | return name + " " + s.child.String(f) 84 | } 85 | -------------------------------------------------------------------------------- /apl/domain/type.go: -------------------------------------------------------------------------------- 1 | package domain 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/ktye/iv/apl" 7 | ) 8 | 9 | // IsType tests if the value has a concrete type. 10 | func IsType(t reflect.Type, child SingleDomain) SingleDomain { 11 | return typ{t, child, false} 12 | } 13 | 14 | // ToType tries converts the value to the given type. 15 | func ToType(t reflect.Type, child SingleDomain) SingleDomain { 16 | return typ{t, child, true} 17 | } 18 | 19 | type typ struct { 20 | t reflect.Type 21 | child SingleDomain 22 | conv bool 23 | } 24 | 25 | func (t typ) String(f apl.Format) string { 26 | name := "type" 27 | if t.conv { 28 | name = "totype" 29 | } 30 | name += " " + t.t.String() 31 | if t.child == nil { 32 | return name 33 | } 34 | return name + " " + t.child.String(f) 35 | } 36 | 37 | func (t typ) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 38 | if reflect.TypeOf(V) == t.t { 39 | return propagate(a, V, t.child) 40 | } 41 | if t.conv == false { 42 | return V, false 43 | } 44 | // TODO: The function should also be used by the convert primitive (⌶). 45 | return V, false // TODO type conversions. 46 | } 47 | -------------------------------------------------------------------------------- /apl/error.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | // Error carries an error value. 4 | // It is used by go routines to signal errors. 5 | // To send err over Channel c, use: c[0]<-Error{e} 6 | type Error struct { 7 | E error 8 | } 9 | 10 | func (e Error) String(f Format) string { 11 | if e.E == nil { 12 | return "" 13 | } 14 | return e.E.Error() 15 | } 16 | func (e Error) Copy() Value { return e } 17 | -------------------------------------------------------------------------------- /apl/eval.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "runtime/debug" 8 | "strings" 9 | ) 10 | 11 | // Program contains a slice of parsed expressions. 12 | type Program []expr 13 | 14 | // Eval executes an apl program. 15 | // It can be called in a loop for every line of input. 16 | func (a *Apl) Eval(p Program) (err error) { 17 | defer func() { 18 | if r := recover(); r != nil { 19 | err = fmt.Errorf("panic: %s\n%s", r, string(debug.Stack())) 20 | } 21 | }() 22 | write := func(val Value) { 23 | switch v := val.(type) { 24 | case Image: 25 | if a.stdimg != nil { 26 | a.stdimg.WriteImage(v) 27 | } else { 28 | fmt.Fprintln(a.stdout, val.String(a.Format)) 29 | } 30 | default: 31 | fmt.Fprintln(a.stdout, val.String(a.Format)) 32 | } 33 | } 34 | 35 | var val Value 36 | for _, expr := range p { 37 | val, err = expr.Eval(a) 38 | if err != nil { 39 | return err 40 | } 41 | if isAssignment(expr) == false { 42 | switch v := val.(type) { 43 | case Channel: 44 | i := 0 45 | for e := range v[0] { 46 | if i == 0 { 47 | i++ 48 | if _, ok := e.(Image); ok && a.stdimg != nil { 49 | a.stdimg.StartLoop() 50 | defer a.stdimg.StopLoop() 51 | } 52 | } 53 | write(e) 54 | } 55 | default: 56 | write(val) 57 | } 58 | } 59 | } 60 | return nil 61 | } 62 | 63 | // EvalProgram evaluates all expressions in the program and returns the values. 64 | func (a *Apl) EvalProgram(p Program) ([]Value, error) { 65 | res := make([]Value, len(p)) 66 | for i, e := range p { 67 | if v, err := e.Eval(a); err != nil { 68 | return nil, err 69 | } else { 70 | res[i] = v 71 | } 72 | } 73 | return res, nil 74 | } 75 | 76 | func (p Program) String(f Format) string { 77 | v := make([]string, len(p)) 78 | for i := range p { 79 | v[i] = p[i].String(f) 80 | } 81 | return strings.Join(v, "⋄") 82 | } 83 | 84 | // EvalFile parses and evalutes from a reader. 85 | // It handles multiline statements. 86 | // The file argument is used only in the error message. 87 | func (a *Apl) EvalFile(r io.Reader, file string) (err error) { 88 | line := 0 89 | defer func() { 90 | if err != nil { 91 | err = fileError{file: file, line: line, err: err} 92 | } 93 | }() 94 | 95 | ok := true 96 | var p Program 97 | b := NewLineBuffer(a) 98 | scanner := bufio.NewScanner(r) 99 | for scanner.Scan() { 100 | line++ 101 | ok, err = b.Add(scanner.Text()) 102 | if err != nil { 103 | return 104 | } 105 | 106 | if ok { 107 | p, err = b.Parse() 108 | if err != nil { 109 | return 110 | } 111 | 112 | err = a.Eval(p) 113 | if err != nil { 114 | return 115 | } 116 | } 117 | } 118 | if ok == false && b.Len() > 0 { 119 | return fmt.Errorf("multiline statement is not terminated") 120 | } 121 | return nil 122 | } 123 | 124 | type fileError struct { 125 | file string 126 | line int 127 | err error 128 | } 129 | 130 | func (f fileError) Error() string { 131 | return fmt.Sprintf("%s:%d: %s", f.file, f.line, f.err.Error()) 132 | } 133 | 134 | type expr interface { 135 | Eval(*Apl) (Value, error) 136 | String(f Format) string 137 | } 138 | 139 | func isAssignment(e expr) bool { 140 | // Assignment is implemented as an operator. 141 | if fn, ok := e.(*function); ok && fn != nil { 142 | if d, ok := fn.Function.(*derived); ok && d.op == "←" { 143 | return true 144 | } 145 | } 146 | return false 147 | } 148 | -------------------------------------------------------------------------------- /apl/fmt_test.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestScanRankArray(t *testing.T) { 11 | 12 | testCases := []struct { 13 | in string 14 | rank int 15 | out []string 16 | }{ 17 | {"", 0, []string{}}, 18 | {"", 1, []string{}}, 19 | {"", 2, []string{}}, 20 | {"1 2 3\n4 5 6\n\n7 8 9\n1 2 3", 0, []string{ 21 | "1", "2", "3", "4", "5", "6", "7", "8", "9", "1", "2", "3", 22 | }}, 23 | {"1 2 3\n4 5 6\n\n7 8 9\n1 2 3", 1, []string{ 24 | "1 2 3 [3]", 25 | "4 5 6 [3]", 26 | "7 8 9 [3]", 27 | "1 2 3 [3]", 28 | }}, 29 | {"1 2 3\n4 5 6\n\n7 8 9\n1 2 3", 2, []string{ 30 | " 1 2 3\n 4 5 6 [2 3]", 31 | " 7 8 9\n 1 2 3 [2 3]", 32 | }}, 33 | {"1 2 3\n4 5 7\n", -1, []string{ 34 | " 1 2 3\n 4 5 7 [2 3]", 35 | }}, 36 | {"1 2 3\n4 5 6", -1, []string{ 37 | " 1 2 3\n 4 5 6 [2 3]", 38 | }}, 39 | {"1 2 3\n4 5 6\n\n7 8 9\n1 2 3\n\n", -1, []string{ 40 | " 1 2 3\n 4 5 6\n\n 7 8 9\n 1 2 3 [2 2 3]", 41 | }}, 42 | {"1 2 3\n4 5 6\n\n7 8 9\n1 2 3", -1, []string{ 43 | " 1 2 3\n 4 5 6\n\n 7 8 9\n 1 2 3 [2 2 3]", 44 | }}, 45 | {"1 2 3\n4 5 6\n\n7 8 9\n1 2 3", 3, []string{ 46 | " 1 2 3\n 4 5 6\n\n 7 8 9\n 1 2 3 [2 2 3]", 47 | }}, 48 | {"1 2 3\n\n4 5 6\n\n7 8 9\n\n1 2 3", 1, []string{ 49 | "1 2 3 [3]", 50 | "4 5 6 [3]", 51 | "7 8 9 [3]", 52 | "1 2 3 [3]", 53 | }}, 54 | {"1 2 3\n\n4 5 6\n\n7 8 9\n\n1 2 3", 2, []string{ 55 | " 1 2 3 [1 3]", 56 | " 4 5 6 [1 3]", 57 | " 7 8 9 [1 3]", 58 | " 1 2 3 [1 3]", 59 | }}, 60 | {"1 2 3\n\n4 5 6\n\n7 8 9\n\n1 2 3", 3, []string{ 61 | " 1 2 3\n\n 4 5 6\n\n 7 8 9\n\n 1 2 3 [4 1 3]", 62 | }}, 63 | {"[[[1 2 3],[4 5 6]],[[7 8 9],[1 2 3]]]", 2, []string{ 64 | " 1 2 3\n 4 5 6 [2 3]", 65 | " 7 8 9\n 1 2 3 [2 3]", 66 | }}, 67 | {"[[1,2,3],[4,5,6]]", -1, []string{ 68 | " 1 2 3\n 4 5 6 [2 3]", 69 | }}, 70 | {"[1,2,3]", -1, []string{ 71 | "1 2 3 [3]", 72 | }}, 73 | {"[1,2,3;4,5,6]", -1, []string{ 74 | " 1 2 3\n 4 5 6 [2 3]", 75 | }}, 76 | } 77 | 78 | for k, tc := range testCases { 79 | a := New(nil) 80 | in := strings.NewReader(tc.in) 81 | for i := 0; ; i++ { 82 | v, err := a.ScanRankArray(in, tc.rank) 83 | if err == io.EOF && i == len(tc.out) { 84 | goto next 85 | } else if err == io.EOF { 86 | t.Fatalf("#%d: expected %d results, got %d", k, len(tc.out), i-1) 87 | } else if err != nil { 88 | t.Fatalf("#%d: %s", k, err) 89 | } else if i == len(tc.out) { 90 | t.Fatalf("#%d: too many output values: %v", k, v) 91 | } else { 92 | s := "" 93 | if _, ok := v.(Array); !ok { 94 | s = v.String(a.Format) 95 | } else { 96 | s = fmt.Sprintf("%v %v", v.String(a.Format), v.(Array).Shape()) 97 | } 98 | if s != tc.out[i] { 99 | t.Fatalf("#%d:%d: expected:\n%s\ngot:\n%s", k, i, tc.out[i], s) 100 | } 101 | } 102 | } 103 | next: 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /apl/http/register.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/ktye/iv/apl" 8 | ) 9 | 10 | func Register(a *apl.Apl, name string) { 11 | pkg := map[string]apl.Value{ 12 | "get": apl.ToFunction(get), 13 | } 14 | if name == "" { 15 | name = "http" 16 | } 17 | a.RegisterPackage(name, pkg) 18 | } 19 | 20 | // http→get returns a channel to read strings from a http connection. 21 | func get(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 22 | addr, ok := R.(apl.String) 23 | if ok == false { 24 | return nil, fmt.Errorf("http get: right argument must be a string") 25 | } 26 | res, err := http.Get(string(addr)) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return apl.LineReader(res.Body), nil 31 | } 32 | -------------------------------------------------------------------------------- /apl/image.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | "image/color" 7 | "image/draw" 8 | ) 9 | 10 | // An ImageWriter is anything that can handle image output. 11 | // Apl's stdimg device uses it. 12 | // A single image can be written directly with WriteImage. 13 | // An animation needs Start- and StopLoop before and after. 14 | type ImageWriter interface { 15 | WriteImage(Image) error 16 | StartLoop() 17 | StopLoop() 18 | } 19 | 20 | // An Image is a raster image. 21 | // 22 | // It can by converting from a numeric array of shape HEIGHT WIDTH. 23 | // An Image is never empty, it always has rank 2. 24 | // It cannot be reshaped, instead reshape it's int array before creating it. 25 | // 26 | // Formats: 27 | // Img ← `img ⌶B B BoolArray (0 White, 1 Black) // TODO or user def, or alpha? 28 | // Img ← `img ⌶(I;P;) I numeric array with values in the range ⎕IO+(0..0xFF) as indexes into P 29 | // P (palette) vector of shape 256 with values as below: 30 | // Img ← `img ⌶N N numeric array, values between 0 and 0xFFFFFFFF (aarrggbb) 31 | // Transparency has the value 0xFF000000, which is inverted compared to the go image library, 32 | // to be able to specify opaque colors in the short form 0xRRGGBB. 33 | // After creation, an image can be indexed and assigned to. 34 | type Image struct { 35 | image.Image 36 | Dims []int 37 | } 38 | 39 | func (i Image) String(f Format) string { 40 | return i.toIntArray().String(f) 41 | } 42 | func (i Image) Copy() Value { return i } // Image is copied by reference. 43 | 44 | func (i Image) At(k int) Value { 45 | ic, idx := NewIdxConverter(i.Dims) 46 | ic.Indexes(k, idx) 47 | return colorValue(i.Image.At(idx[0], idx[1])) 48 | } 49 | func (i Image) Shape() []int { 50 | return i.Dims 51 | } 52 | func (i Image) Size() int { 53 | return Prod(i.Dims) 54 | } 55 | func (i Image) Set(k int, v Value) error { 56 | num, ok := v.(Number) 57 | if ok == false { 58 | return fmt.Errorf("img set: value must be a number: %T", v) 59 | } 60 | c, ok := num.ToIndex() 61 | if ok == false { 62 | return fmt.Errorf("img set: value must be an integer: %T", v) 63 | } 64 | y := k / i.Dims[1] 65 | x := k - y*i.Dims[1] 66 | r := i.Bounds() 67 | if d, ok := i.Image.(draw.Image); ok { 68 | d.Set(x+r.Min.X, y+r.Min.Y, toColor(c)) 69 | return nil 70 | } 71 | return fmt.Errorf("img: image is not settable: %T", i.Image) 72 | } 73 | 74 | func (i Image) toIntArray() IntArray { 75 | ints := make([]int, Prod(i.Dims)) 76 | shape := make([]int, len(i.Dims)) 77 | copy(shape, i.Dims) 78 | idx := 0 79 | r := i.Image.Bounds() 80 | for y := r.Min.Y; y < r.Max.Y; y++ { 81 | for x := r.Min.X; x < r.Max.X; x++ { 82 | ints[idx] = int(colorValue(i.Image.At(x, y))) 83 | idx++ 84 | } 85 | } 86 | return IntArray{Dims: shape, Ints: ints} 87 | } 88 | 89 | // ColorValue converts a Color to an Int. 90 | func colorValue(c color.Color) Int { 91 | r, g, b, a := c.RGBA() // uint32 premultiplied with alpha. 92 | u := (255-(a>>8))<<16 | r&0xFF00<<8 | g&0xFF00 | b>>8 93 | return Int(u) 94 | } 95 | func toColor(i int) color.Color { 96 | u := uint32(i) 97 | return color.RGBA{uint8(u & 0xFF0000 >> 16), uint8(u & 0xFF00 >> 8), uint8(u & 0xFF), ^uint8(u >> 24)} 98 | } 99 | -------------------------------------------------------------------------------- /apl/index.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // IdxSpec is the value of an index specification. 9 | // For an axis specification this must be a length 1 slice 10 | // and the value is an apl.Float. 11 | // For an index specification this is an array and each value 12 | // is either an EmptyArray or an IntArray. 13 | // IdxSpec is origin dependend. 14 | type IdxSpec []Value 15 | 16 | func (x IdxSpec) String(f Format) string { 17 | if x == nil { 18 | return "[nil]" 19 | } 20 | v := make([]string, len(x)) 21 | for i, e := range x { 22 | v[i] = e.String(f) 23 | } 24 | return "[" + strings.Join(v, ";") + "]" 25 | } 26 | func (x IdxSpec) Copy() Value { 27 | r := make(IdxSpec, len(x)) 28 | for i := range r { 29 | r[i] = x[i].Copy() 30 | } 31 | return r 32 | } 33 | 34 | func (x IdxSpec) Eval(a *Apl) (Value, error) { 35 | return x, nil 36 | } 37 | 38 | // Shape returns the result shape of the index specification applied to an array with shape src. 39 | func (x IdxSpec) Shape(src []int) ([]int, error) { 40 | return nil, fmt.Errorf("TODO apl.IdxSpec.Shape") 41 | } 42 | 43 | // IdxSpec represents an expression of an index specification. 44 | // That is what is in square brackets following an array. 45 | type idxSpec []expr 46 | 47 | func (x idxSpec) String(f Format) string { 48 | if x == nil { 49 | return "[nil]" 50 | } 51 | v := make([]string, len(x)) 52 | for i, e := range x { 53 | v[i] = e.String(f) 54 | } 55 | return "[" + strings.Join(v, ";") + "]" 56 | } 57 | 58 | func (x idxSpec) Eval(a *Apl) (Value, error) { 59 | idx := make(IdxSpec, len(x)) 60 | for i, e := range x { 61 | if v, err := e.Eval(a); err != nil { 62 | return nil, err 63 | } else { 64 | idx[i] = v 65 | } 66 | } 67 | return idx, nil 68 | } 69 | 70 | // Axis combines the right argument with an axis specification. 71 | type Axis struct { 72 | R Value 73 | A Value 74 | } 75 | 76 | func (ax Axis) String(f Format) string { 77 | return fmt.Sprintf("[%s]%s", ax.A.String(f), ax.R.String(f)) 78 | } 79 | func (ax Axis) Copy() Value { 80 | r := Axis{} 81 | if ax.R != nil { 82 | r.R = ax.R.Copy() 83 | } 84 | if ax.A != nil { 85 | r.A = ax.A.Copy() 86 | } 87 | return r 88 | } 89 | -------------------------------------------------------------------------------- /apl/io/README.md: -------------------------------------------------------------------------------- 1 | # Package io provides input and output streams 2 | 3 | Linking it into APL leads to an unsafe system. 4 | 5 | Io overloads several *primitive functions*: 6 | ``` 7 | < filename returns a Channel reading from a file 8 | < 0 returns a Channel reading from stdin 9 | !`ls execute program return a channel 10 | !(`ls`-l) same with arguments 11 | `cat!A same reading input from A (String method) or channel (pipe) 12 | `file max { 59 | max = 1 + d 60 | } 61 | } 62 | } 63 | return max 64 | } 65 | 66 | func (l List) getset(idx []int, v Value) (Value, error) { 67 | if len(idx) == 0 { 68 | return nil, fmt.Errorf("empty index") 69 | } 70 | for i, k := range idx { 71 | if k < 0 || k >= len(l) { 72 | return nil, fmt.Errorf("index out of range") 73 | } 74 | if i == len(idx)-1 { 75 | if v != nil { 76 | l[k] = v 77 | return nil, nil 78 | } else { 79 | return l[k], nil 80 | } 81 | } 82 | if lst, ok := l[k].(List); ok == false { 83 | return nil, fmt.Errorf("index is too deep") 84 | } else { 85 | l = lst 86 | } 87 | } 88 | return nil, fmt.Errorf("not reached") 89 | } 90 | 91 | // jsonString formats the list as a json object. 92 | func (l List) jsonString(f Format) string { 93 | var b strings.Builder 94 | b.WriteRune('[') 95 | for i, v := range l { 96 | if i > 0 { 97 | b.WriteRune(',') 98 | } 99 | b.WriteString(v.String(f)) 100 | } 101 | b.WriteRune(']') 102 | return b.String() 103 | } 104 | 105 | type list []expr 106 | 107 | func (l list) Eval(a *Apl) (Value, error) { 108 | lst := make(List, len(l)) 109 | var err error 110 | for i := range lst { 111 | if l[i] == nil { 112 | lst[i] = EmptyArray{} 113 | continue 114 | } 115 | lst[i], err = l[i].Eval(a) 116 | if err != nil { 117 | return nil, err 118 | } 119 | } 120 | return lst, nil 121 | } 122 | 123 | func (l list) String(f Format) string { 124 | var buf strings.Builder 125 | buf.WriteRune('(') 126 | for i := range l { 127 | buf.WriteString(l[i].String(f)) 128 | buf.WriteRune(';') 129 | } 130 | buf.WriteRune(')') 131 | return buf.String() 132 | } 133 | -------------------------------------------------------------------------------- /apl/multiline.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/ktye/iv/apl/scan" 8 | ) 9 | 10 | // ParseLines parses multiple lines separated by newline, that may contain continuation lines. 11 | // Continuation lines are only allowed for lambda functions. 12 | func (a *Apl) ParseLines(lines string) (Program, error) { 13 | b := NewLineBuffer(a) 14 | v := strings.Split(lines, "\n") 15 | for i, s := range v { 16 | if ok, err := b.Add(s); err != nil { 17 | return nil, err 18 | } else if i == len(v)-1 { 19 | if ok == false { 20 | return nil, fmt.Errorf("unbalanced {") 21 | } 22 | return b.Parse() 23 | } 24 | } 25 | return nil, nil 26 | } 27 | 28 | // LineBuffer buffers multiline statements for lambda functions. 29 | type LineBuffer struct { 30 | a *Apl 31 | tokens []scan.Token 32 | level int 33 | } 34 | 35 | func NewLineBuffer(a *Apl) *LineBuffer { 36 | return &LineBuffer{a: a} 37 | } 38 | 39 | // Add a line to the buffer. 40 | // The function returns ok, if the line is complete and can be parsed. 41 | func (b *LineBuffer) Add(line string) (bool, error) { 42 | if b.a == nil { 43 | return false, fmt.Errorf("linebuffer is not initialized (no APL)") 44 | } 45 | tokens, err := b.a.Scan(line) 46 | if err != nil { 47 | b.reset() 48 | return false, err 49 | } 50 | if len(tokens) == 0 { 51 | return false, nil 52 | } 53 | 54 | // Join with diamonds. Ommit the diamond if the last token is LeftBrace 55 | // or the next token is a RightBrace. 56 | diamond := true 57 | if len(b.tokens) == 0 { 58 | diamond = false 59 | } else if b.tokens[len(b.tokens)-1].T == scan.LeftBrace { 60 | diamond = false 61 | } 62 | if len(tokens) > 0 && diamond == true { 63 | b.tokens = append(b.tokens, scan.Token{T: scan.Diamond, S: "⋄"}) 64 | } 65 | b.tokens = append(b.tokens, tokens...) 66 | 67 | for _, t := range tokens { 68 | if t.T == scan.LeftBrace { 69 | b.level++ 70 | } else if t.T == scan.RightBrace { 71 | b.level-- 72 | if b.level < 0 { 73 | b.reset() 74 | return false, fmt.Errorf("too many }") 75 | } 76 | } 77 | } 78 | if b.level == 0 { 79 | return true, nil 80 | } 81 | return false, nil 82 | } 83 | 84 | // Parse parses the tokens in the buffer. 85 | // Lines must be pushed to the buffer with Add and Parse should only be called if Add returned true. 86 | func (b *LineBuffer) Parse() (Program, error) { 87 | defer b.reset() 88 | return b.a.parse(b.tokens) 89 | } 90 | 91 | func (b *LineBuffer) Len() int { 92 | return len(b.tokens) 93 | } 94 | 95 | func (b *LineBuffer) reset() { 96 | b.level = 0 97 | if len(b.tokens) > 0 { 98 | b.tokens = b.tokens[:0] 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /apl/numbers/complexs.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | ) 8 | 9 | // ComplexArray is a uniform array of complex128 10 | type ComplexArray struct { 11 | Dims []int 12 | Cmplx []complex128 13 | } 14 | 15 | func (f ComplexArray) String(af apl.Format) string { 16 | return apl.ArrayString(af, f) 17 | } 18 | 19 | func (f ComplexArray) Copy() apl.Value { 20 | r := ComplexArray{Dims: apl.CopyShape(f), Cmplx: make([]complex128, len(f.Cmplx))} 21 | copy(r.Cmplx, f.Cmplx) 22 | return f 23 | } 24 | 25 | func (f ComplexArray) At(i int) apl.Value { 26 | return Complex(f.Cmplx[i]) 27 | } 28 | 29 | func (f ComplexArray) Shape() []int { 30 | return f.Dims 31 | } 32 | 33 | func (f ComplexArray) Size() int { 34 | return len(f.Cmplx) 35 | } 36 | 37 | func (f ComplexArray) Zero() apl.Value { 38 | return Complex(0.0) 39 | } 40 | 41 | func (f ComplexArray) Set(i int, v apl.Value) error { 42 | if i < 0 || i > len(f.Cmplx) { 43 | return fmt.Errorf("index out of range") 44 | } 45 | if c, ok := v.(Complex); ok { 46 | f.Cmplx[i] = complex128(c) 47 | return nil 48 | } 49 | return fmt.Errorf("cannot assign %T to ComplexArray", v) 50 | } 51 | 52 | func (f ComplexArray) Make(shape []int) apl.Uniform { 53 | return ComplexArray{ 54 | Dims: shape, 55 | Cmplx: make([]complex128, prod(shape)), 56 | } 57 | } 58 | 59 | func makeComplexArray(v []apl.Value) ComplexArray { 60 | f := make([]complex128, len(v)) 61 | for i, e := range v { 62 | f[i] = complex128(e.(Complex)) 63 | } 64 | return ComplexArray{ 65 | Dims: []int{len(v)}, 66 | Cmplx: f, 67 | } 68 | } 69 | 70 | func (f ComplexArray) Reshape(shape []int) apl.Value { 71 | res := ComplexArray{ 72 | Dims: shape, 73 | Cmplx: make([]complex128, prod(shape)), 74 | } 75 | k := 0 76 | for i := range res.Cmplx { 77 | res.Cmplx[i] = f.Cmplx[k] 78 | k++ 79 | if k == len(f.Cmplx) { 80 | k = 0 81 | } 82 | } 83 | return res 84 | } 85 | -------------------------------------------------------------------------------- /apl/numbers/exception.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "math" 5 | "math/cmplx" 6 | 7 | "github.com/ktye/iv/apl" 8 | ) 9 | 10 | const ( 11 | NaN exception = "NaN" 12 | Inf exception = "∞" 13 | NegInf exception = "¯∞" 14 | ) 15 | 16 | type exception string 17 | 18 | func (e exception) String(f apl.Format) string { 19 | return string(e) 20 | } 21 | func (e exception) Copy() apl.Value { return e } 22 | 23 | func isException(n apl.Number) (exception, bool) { 24 | if f, ok := n.(Float); ok { 25 | if math.IsNaN(float64(f)) { 26 | return NaN, true 27 | } 28 | if math.IsInf(float64(f), 1) { 29 | return Inf, true 30 | } 31 | if math.IsInf(float64(f), -1) { 32 | return NegInf, true 33 | } 34 | } 35 | if c, ok := n.(Complex); ok { 36 | if cmplx.IsNaN(complex128(c)) { 37 | return NaN, true 38 | } 39 | if cmplx.IsInf(complex128(c)) { 40 | return Inf, true 41 | } 42 | } 43 | return "", false 44 | } 45 | -------------------------------------------------------------------------------- /apl/numbers/floats.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | ) 8 | 9 | // FloatArray is a uniform array of float64. 10 | type FloatArray struct { 11 | Dims []int 12 | Floats []float64 13 | } 14 | 15 | func (f FloatArray) String(af apl.Format) string { 16 | return apl.ArrayString(af, f) 17 | } 18 | func (f FloatArray) Copy() apl.Value { 19 | r := FloatArray{Dims: apl.CopyShape(f), Floats: make([]float64, len(f.Floats))} 20 | copy(r.Floats, f.Floats) 21 | return r 22 | } 23 | 24 | func (f FloatArray) At(i int) apl.Value { 25 | return Float(f.Floats[i]) 26 | } 27 | 28 | func (f FloatArray) Shape() []int { 29 | return f.Dims 30 | } 31 | 32 | func (f FloatArray) Size() int { 33 | return len(f.Floats) 34 | } 35 | 36 | func (f FloatArray) Zero() apl.Value { 37 | return Float(0.0) 38 | } 39 | 40 | func (f FloatArray) Set(i int, v apl.Value) error { 41 | if i < 0 || i > len(f.Floats) { 42 | return fmt.Errorf("index out of range") 43 | } 44 | if c, ok := v.(Float); ok { 45 | f.Floats[i] = float64(c) 46 | return nil 47 | } 48 | return fmt.Errorf("cannot assign %T to FloatArray", v) 49 | } 50 | 51 | func (f FloatArray) Make(shape []int) apl.Uniform { 52 | return FloatArray{ 53 | Dims: shape, 54 | Floats: make([]float64, prod(shape)), 55 | } 56 | } 57 | 58 | func makeFloatArray(v []apl.Value) FloatArray { 59 | f := make([]float64, len(v)) 60 | for i, e := range v { 61 | f[i] = float64(e.(Float)) 62 | } 63 | return FloatArray{ 64 | Dims: []int{len(v)}, 65 | Floats: f, 66 | } 67 | } 68 | 69 | func (f FloatArray) Reshape(shape []int) apl.Value { 70 | res := FloatArray{ 71 | Dims: shape, 72 | Floats: make([]float64, prod(shape)), 73 | } 74 | k := 0 75 | for i := range res.Floats { 76 | res.Floats[i] = f.Floats[k] 77 | k++ 78 | if k == len(f.Floats) { 79 | k = 0 80 | } 81 | } 82 | return res 83 | } 84 | 85 | func prod(shape []int) int { 86 | if len(shape) == 0 { 87 | return 0 88 | } 89 | n := shape[0] 90 | for i := 1; i < len(shape); i++ { 91 | n *= shape[i] 92 | } 93 | return n 94 | } 95 | -------------------------------------------------------------------------------- /apl/numbers/numbers_test.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | "time" 7 | 8 | "github.com/ktye/iv/apl" 9 | ) 10 | 11 | func TestParse(t *testing.T) { 12 | testCases := []struct { 13 | s string 14 | n apl.Number 15 | }{ 16 | {"1", apl.Int(1)}, 17 | {"1b", apl.Bool(true)}, 18 | {"¯2", apl.Int(-2)}, 19 | {"¯2.0", Float(-2)}, 20 | {"2.", Float(2)}, 21 | {"3J¯2.0", Complex(complex(3, -2))}, 22 | {"5a90", Complex(complex(0, 5))}, 23 | {"3.12E¯2", Float(0.0312)}, 24 | {".5", Float(0.5)}, 25 | {"¯.3", Float(-0.3)}, 26 | {"2014.04.02", Time(time.Date(2014, 4, 2, 0, 0, 0, 0, time.UTC))}, 27 | {"2014.04.02T09.37.22", Time(time.Date(2014, 4, 2, 9, 37, 22, 0, time.UTC))}, 28 | {"10s", Time(y0.Add(10 * time.Second))}, 29 | } 30 | 31 | a := apl.New(nil) 32 | Register(a) 33 | 34 | for k, tc := range testCases { 35 | ne, err := a.Tower.Parse(tc.s) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | n, err := ne.Eval(a) 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | if reflect.TypeOf(n) != reflect.TypeOf(tc.n) { 44 | t.Fatalf("#%d: %s: expected %T got %T", k, tc.s, tc.n, n) 45 | } 46 | if n != tc.n { 47 | t.Fatalf("#%d: %s: numbers are not equal: %v, %v", k, tc.s, tc.n, n) 48 | } 49 | } 50 | } 51 | 52 | func TestSameType(t *testing.T) { 53 | a := apl.New(nil) 54 | Register(a) 55 | 56 | testCases := []struct { 57 | a, b apl.Number 58 | c, d apl.Number 59 | }{ 60 | {apl.Bool(false), apl.Bool(true), apl.Bool(false), apl.Bool(true)}, 61 | {apl.Bool(false), apl.Int(1), apl.Int(0), apl.Int(1)}, 62 | {apl.Int(1), apl.Bool(false), apl.Int(1), apl.Int(0)}, 63 | {apl.Int(1), apl.Int(2), apl.Int(1), apl.Int(2)}, 64 | {apl.Bool(true), Float(3), Float(1), Float(3)}, 65 | {apl.Int(0), Float(3), Float(0), Float(3)}, 66 | {Float(3), apl.Int(4), Float(3), Float(4)}, 67 | {apl.Int(2), Complex(3 + 1i), Complex(2), Complex(3 + 1i)}, 68 | {Complex(1 + 2i), Float(3), Complex(1 + 2i), Complex(3)}, 69 | } 70 | 71 | for n, tc := range testCases { 72 | c, d, err := a.Tower.SameType(tc.a, tc.b) 73 | if err != nil { 74 | t.Fatalf("#%d: %s", n, err) 75 | } 76 | if reflect.TypeOf(c) != reflect.TypeOf(d) { 77 | t.Fatalf("not the same type: %T %T", c, d) 78 | } 79 | if c != tc.c { 80 | t.Fatalf("expected %v got %v", tc.c, c) 81 | } 82 | if d != tc.d { 83 | t.Fatalf("expected %v got %v", tc.d, d) 84 | } 85 | } 86 | } 87 | 88 | func TestImport(t *testing.T) { 89 | a := apl.New(nil) 90 | Register(a) 91 | 92 | testCases := []struct { 93 | i apl.Number 94 | n apl.Number 95 | }{ 96 | {apl.Bool(false), Float(0)}, 97 | {apl.Bool(true), Float(1)}, 98 | {apl.Int(0), Float(0)}, 99 | {apl.Int(1), Float(1)}, 100 | {apl.Int(2), Float(2)}, 101 | {apl.Int(-1), Float(-1)}, 102 | } 103 | for _, tc := range testCases { 104 | n := a.Tower.Import(tc.i) 105 | if reflect.TypeOf(n) != reflect.TypeOf(tc.n) { 106 | t.Fatal("wrong type") 107 | } 108 | if n != tc.n { 109 | t.Fatal("wrong value") 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /apl/numbers/register.go: -------------------------------------------------------------------------------- 1 | // Package number defines the basic numeric types for APL. 2 | // These are Integer, Float and Complex. 3 | package numbers 4 | 5 | import ( 6 | "reflect" 7 | "time" 8 | 9 | "github.com/ktye/iv/apl" 10 | ) 11 | 12 | // Register sets the default numeric tower Integer->Float->Complex. 13 | func Register(a *apl.Apl) { 14 | if err := a.SetTower(newTower()); err != nil { 15 | panic(err) 16 | } 17 | } 18 | 19 | func newTower() apl.Tower { 20 | m := make(map[reflect.Type]*apl.Numeric) 21 | m[reflect.TypeOf(Float(0))] = &apl.Numeric{ 22 | Class: 0, 23 | Parse: ParseFloat, 24 | Uptype: floatToComplex, 25 | } 26 | m[reflect.TypeOf(Complex(0))] = &apl.Numeric{ 27 | Class: 1, 28 | Parse: ParseComplex, 29 | Uptype: func(n apl.Number) (apl.Number, bool) { 30 | // Uptype converts a number to seconds, if the imag part is 0 31 | if imag(complex128(n.(Complex))) != 0 { 32 | return nil, false 33 | } 34 | d := time.Duration(int64(1e9 * real(complex128(n.(Complex))))) 35 | return Time(y0.Add(d)), true 36 | }, 37 | } 38 | m[reflect.TypeOf(Time{})] = &apl.Numeric{ 39 | Class: 2, 40 | Parse: ParseTime, 41 | Uptype: func(n apl.Number) (apl.Number, bool) { return n, false }, 42 | } 43 | t := apl.Tower{ 44 | Numbers: m, 45 | Import: func(n apl.Number) apl.Number { 46 | if b, ok := n.(apl.Bool); ok { 47 | if b { 48 | return Float(1) 49 | } 50 | return Float(0) 51 | } else if n, ok := n.(apl.Int); ok { 52 | return Float(n) 53 | } 54 | return n 55 | }, 56 | Uniform: makeUniform, 57 | } 58 | return t 59 | } 60 | 61 | func makeUniform(v []apl.Value) (apl.Value, bool) { 62 | if len(v) == 0 { 63 | return nil, false 64 | } 65 | if t := reflect.TypeOf(v[0]); t == reflect.TypeOf(apl.Bool(false)) { 66 | return makeBoolArray(v), true 67 | } else if t := reflect.TypeOf(v[0]); t == reflect.TypeOf(apl.Int(0)) { 68 | return makeIndexArray(v), true 69 | } else if t == reflect.TypeOf(Float(0.0)) { 70 | return makeFloatArray(v), true 71 | } else if t == reflect.TypeOf(Complex(0)) { 72 | return makeComplexArray(v), true 73 | } else if t == reflect.TypeOf(y0) { 74 | return makeTimeArray(v), true 75 | } 76 | return nil, false 77 | } 78 | 79 | func makeBoolArray(v []apl.Value) apl.BoolArray { 80 | f := make([]bool, len(v)) 81 | for i, e := range v { 82 | f[i] = bool(e.(apl.Bool)) 83 | } 84 | return apl.BoolArray{ 85 | Dims: []int{len(v)}, 86 | Bools: f, 87 | } 88 | } 89 | func makeIndexArray(v []apl.Value) apl.IntArray { 90 | f := make([]int, len(v)) 91 | for i, e := range v { 92 | f[i] = int(e.(apl.Int)) 93 | } 94 | return apl.IntArray{ 95 | Dims: []int{len(v)}, 96 | Ints: f, 97 | } 98 | } 99 | 100 | func getformat(f apl.Format, num apl.Value) (string, bool) { 101 | if f.Fmt == nil { 102 | return "", false 103 | } 104 | s := f.Fmt[reflect.TypeOf(num)] 105 | if len(s) > 0 && s[0] == '-' { 106 | return s[1:], true 107 | } 108 | if f.PP < -1 { 109 | return "", true // intended for external interchange (full prec, with -). 110 | } 111 | return s, false 112 | } 113 | -------------------------------------------------------------------------------- /apl/numbers/times.go: -------------------------------------------------------------------------------- 1 | package numbers 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/ktye/iv/apl" 8 | ) 9 | 10 | // TimeArray is a uniform array of time.Time. 11 | type TimeArray struct { 12 | Dims []int 13 | Times []time.Time 14 | } 15 | 16 | func (t TimeArray) String(f apl.Format) string { 17 | return apl.ArrayString(f, t) 18 | } 19 | 20 | func (t TimeArray) Copy() apl.Value { 21 | r := TimeArray{Dims: apl.CopyShape(t), Times: make([]time.Time, len(t.Times))} 22 | copy(r.Times, t.Times) 23 | return r 24 | } 25 | 26 | func (t TimeArray) At(i int) apl.Value { 27 | return Time(t.Times[i]) 28 | } 29 | 30 | func (t TimeArray) Shape() []int { 31 | return t.Dims 32 | } 33 | 34 | func (t TimeArray) Size() int { 35 | return len(t.Times) 36 | } 37 | 38 | func (t TimeArray) Zero() apl.Value { 39 | return Time(y0) 40 | } 41 | 42 | func (t TimeArray) Set(i int, v apl.Value) error { 43 | if i < 0 || i > len(t.Times) { 44 | return fmt.Errorf("index out of range") 45 | } 46 | if c, ok := v.(Time); ok { 47 | t.Times[i] = time.Time(c) 48 | return nil 49 | } 50 | return fmt.Errorf("cannot assign %T to TimeArray", v) 51 | } 52 | 53 | func (t TimeArray) Reshape(shape []int) apl.Value { 54 | res := TimeArray{ 55 | Dims: shape, 56 | Times: make([]time.Time, prod(shape)), 57 | } 58 | k := 0 59 | for i := range res.Times { 60 | res.Times[i] = t.Times[k] 61 | k++ 62 | if k == len(t.Times) { 63 | k = 0 64 | } 65 | } 66 | return res 67 | } 68 | 69 | func (t TimeArray) Make(shape []int) apl.Uniform { 70 | return TimeArray{ 71 | Dims: shape, 72 | Times: make([]time.Time, prod(shape)), 73 | } 74 | } 75 | 76 | func makeTimeArray(v []apl.Value) TimeArray { 77 | t := make([]time.Time, len(v)) 78 | for i, e := range v { 79 | t[i] = time.Time(e.(Time)) 80 | } 81 | return TimeArray{ 82 | Dims: []int{len(v)}, 83 | Times: t, 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /apl/object.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "text/tabwriter" 7 | ) 8 | 9 | // Object is a compound type that has keys and values. 10 | // 11 | // Values are accessed by indexing with keys. 12 | // Object[Key] 13 | // Keys are usually strings, but dont have to be. 14 | // To set a key, use indexed assignment: 15 | // Object[Name]←X 16 | // This also works for vectors 17 | // Object[`k1`k2`k3] ← 5 6 7 18 | // 19 | // Keys are returned by #Object. 20 | // Number of keys can also be obtained by ⍴Object. 21 | // 22 | // Indexing by vector returns a Dict with the specified keys. 23 | // Object["key1" "key2"]. 24 | // 25 | // Method calls (calling a function stored in a key) or a go method 26 | // for an xgo object cannot be applied directly: 27 | // Object[`f] R ⍝ cannot be parsed 28 | // Instead, assign it to a function variable, or commute: 29 | // f←Object[`f] ⋄ f R 30 | // Object[`f]⍨R 31 | type Object interface { 32 | Value 33 | Keys() []Value 34 | At(Value) Value 35 | Set(Value, Value) error 36 | } 37 | 38 | // Dict is a dictionary object. 39 | // A Dict is created with the L#R, where 40 | // L is a key or a vector of keys and R conforming values. 41 | // Dicts can be indexed with their keys. 42 | // Example: 43 | // D←`alpha#1 2 3 ⍝ Single key 44 | // D←`a`b`c#1 2 3 ⍝ 3 Keys 45 | // D[`a] ⍝ returns value 1 46 | // D[`a`c] ⍝ returns a dict with 2 keys 47 | type Dict struct { 48 | K []Value 49 | M map[Value]Value 50 | } 51 | 52 | func (d *Dict) Keys() []Value { 53 | return d.K 54 | } 55 | 56 | func (d *Dict) At(key Value) Value { 57 | if d.M == nil { 58 | return nil 59 | } 60 | return d.M[key] 61 | } 62 | 63 | // Set updates the value for the given key, or creates a new one, 64 | // if the key does not exist. 65 | // Keys must be valid variable names. 66 | func (d *Dict) Set(key Value, v Value) error { 67 | if d.M == nil { 68 | d.M = make(map[Value]Value) 69 | } 70 | if _, ok := d.M[key]; ok == false { 71 | d.K = append(d.K, key.Copy()) 72 | } 73 | d.M[key.Copy()] = v.Copy() 74 | return nil 75 | } 76 | 77 | func (d *Dict) String(f Format) string { 78 | if f.PP == -2 { 79 | return d.jsonString(f) 80 | } else if f.PP == -3 { 81 | return d.matString(f) 82 | } 83 | var buf strings.Builder 84 | tw := tabwriter.NewWriter(&buf, 1, 0, 1, ' ', 0) 85 | for _, k := range d.K { 86 | fmt.Fprintf(tw, "%s:\t%s\n", k.String(f), d.M[k].String(f)) 87 | } 88 | tw.Flush() 89 | s := buf.String() 90 | if len(s) > 0 && s[len(s)-1] == '\n' { 91 | return s[:len(s)-1] 92 | } 93 | return s 94 | } 95 | 96 | func (d *Dict) Copy() Value { 97 | r := Dict{} 98 | if d.K != nil { 99 | r.K = make([]Value, len(d.K)) 100 | for i := range d.K { 101 | r.K[i] = d.K[i].Copy() 102 | } 103 | } 104 | if d.M != nil { 105 | r.M = make(map[Value]Value) 106 | for k, v := range d.M { 107 | r.M[k.Copy()] = v.Copy() 108 | } 109 | } 110 | return &r 111 | } 112 | 113 | func (d *Dict) jsonString(f Format) string { 114 | var b strings.Builder 115 | b.WriteRune('{') 116 | keys := d.Keys() 117 | for i, key := range keys { 118 | if i > 0 { 119 | b.WriteRune(',') 120 | } 121 | k := key.String(f) 122 | val := d.At(key) 123 | v := val.String(f) 124 | b.WriteString(k) 125 | b.WriteRune(':') 126 | b.WriteString(v) 127 | } 128 | b.WriteRune('}') 129 | return b.String() 130 | } 131 | 132 | func (d *Dict) matString(f Format) string { 133 | var b strings.Builder 134 | b.WriteString("struct(") 135 | keys := d.Keys() 136 | for i, key := range keys { 137 | if i > 0 { 138 | b.WriteRune(',') 139 | } 140 | k := key.String(f) 141 | val := d.At(key) 142 | v := val.String(f) 143 | b.WriteString(k) 144 | b.WriteRune(',') 145 | b.WriteString(v) 146 | } 147 | b.WriteRune(')') 148 | return b.String() 149 | } 150 | 151 | func (a *Apl) ParseDict(prototype Value, s string) (*Dict, error) { 152 | if prototype != nil { 153 | _, ok := prototype.(*Dict) 154 | if ok == false { 155 | return nil, fmt.Errorf("ParseDict: prototype is not a dict: %T", prototype) 156 | } 157 | } 158 | return nil, fmt.Errorf("TODO ParseDict") 159 | } 160 | -------------------------------------------------------------------------------- /apl/operators/axis.go: -------------------------------------------------------------------------------- 1 | package operators 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | func init() { 11 | register(operator{ 12 | symbol: "⍂", 13 | Domain: DyadicOp(nil), 14 | doc: "axis specification", 15 | derived: axis, 16 | selection: selection(axis), 17 | }) 18 | } 19 | 20 | func axis(a *apl.Apl, f, g apl.Value) apl.Function { 21 | derived := func(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 22 | if p, ok := f.(apl.Function); ok { 23 | return p.Call(a, L, apl.Axis{R: R, A: g}) 24 | } else { 25 | return nil, fmt.Errorf("axis: expected primitive on the left: %T", f) 26 | } 27 | } 28 | return function(derived) 29 | } 30 | 31 | // splitAxis returns ax.R and converts ax.A to []int taking account of index origin. 32 | // It R is not an axis it returns R and nil. 33 | func splitAxis(a *apl.Apl, R apl.Value) (apl.Value, []int, error) { 34 | ax, ok := R.(apl.Axis) 35 | if ok == false { 36 | return R, nil, nil 37 | } 38 | if _, ok := ax.A.(apl.EmptyArray); ok { 39 | return ax.R, nil, nil 40 | } 41 | to := ToIndexArray(nil) 42 | X, ok := to.To(a, ax.A) 43 | if ok == false { 44 | return nil, nil, fmt.Errorf("axis is not an index array") 45 | } 46 | ar := X.(apl.IntArray) 47 | shape := ar.Shape() 48 | if len(shape) != 1 { 49 | return nil, nil, fmt.Errorf("axis has wrong shape: %d", len(shape)) 50 | } 51 | x := make([]int, len(ar.Ints)) 52 | for i, n := range ar.Ints { 53 | x[i] = n - a.Origin 54 | } 55 | return ax.R, x, nil 56 | } 57 | -------------------------------------------------------------------------------- /apl/operators/commute.go: -------------------------------------------------------------------------------- 1 | package operators 2 | 3 | import ( 4 | "github.com/ktye/iv/apl" 5 | . "github.com/ktye/iv/apl/domain" 6 | ) 7 | 8 | func init() { 9 | register(operator{ 10 | symbol: "⍨", 11 | Domain: MonadicOp(Function(nil)), 12 | doc: "commute, duplicate", 13 | derived: commute, 14 | }) 15 | } 16 | 17 | func commute(a *apl.Apl, f, _ apl.Value) apl.Function { 18 | derived := func(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 19 | f := f.(apl.Function) 20 | if L == nil { 21 | L = R.Copy() 22 | } 23 | return f.Call(a, R, L) 24 | } 25 | return function(derived) 26 | } 27 | -------------------------------------------------------------------------------- /apl/operators/identity.go: -------------------------------------------------------------------------------- 1 | package operators 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/ktye/iv/apl" 7 | "github.com/ktye/iv/apl/numbers" 8 | ) 9 | 10 | // identityItem returns the identity item for the given function f, when 11 | // the function is applied as f/⍳0. 12 | func identityItem(f apl.Value) apl.Value { 13 | // Table from APL2: p 211, DyaRef p 170 14 | if p, ok := f.(apl.Primitive); ok { 15 | switch p { 16 | case "+", "-", "|", "∨", "<", ">", "≠", "⊤", "∪", "⌽", "⊖": 17 | return apl.Int(0) 18 | case "×", "÷", "*", "!", "^", "∧", "≤", "=", "≥", "/", "⌿", `\`, `⍀`: 19 | return apl.Int(1) 20 | case "⌊": 21 | return numbers.Float(-math.MaxFloat64) 22 | case "⌈": 23 | return numbers.Float(math.MaxFloat64) 24 | } 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /apl/operators/jot.go: -------------------------------------------------------------------------------- 1 | package operators 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | func init() { 11 | register(operator{ 12 | symbol: "∘", 13 | Domain: DyadicOp(Split(nil, nil)), 14 | doc: "compose", 15 | derived: compose, 16 | }) 17 | } 18 | 19 | func compose(a *apl.Apl, f, g apl.Value) apl.Function { 20 | derived := func(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 21 | fn, isfunc := f.(apl.Function) 22 | gn, isgunc := g.(apl.Function) 23 | if L == nil { 24 | if isfunc && isgunc { 25 | // Form 1: f∘g R 26 | v, err := gn.Call(a, nil, R) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return fn.Call(a, nil, v.Copy()) 31 | } else if isgunc { 32 | // Form II: A∘g R 33 | return gn.Call(a, f, R) 34 | } else if isfunc { 35 | // Form III: (f∘X) R 36 | return fn.Call(a, R, g) 37 | } 38 | } else { 39 | if isfunc && isgunc { 40 | // Form IV: L f∘g R 41 | v, err := gn.Call(a, nil, R) 42 | if err != nil { 43 | return nil, err 44 | } 45 | return fn.Call(a, L, v.Copy()) 46 | } 47 | } 48 | return nil, fmt.Errorf("compose: cannot handle %T %T ∘ %T %T", L, f, g, R) 49 | } 50 | return function(derived) 51 | } 52 | -------------------------------------------------------------------------------- /apl/operators/power.go: -------------------------------------------------------------------------------- 1 | package operators 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | func init() { 11 | register(operator{ 12 | symbol: "⍣", 13 | Domain: DyadicOp(Split(Function(nil), nil)), 14 | doc: "power", 15 | derived: power, 16 | }) 17 | } 18 | 19 | // TODO: should there be a limit? How to set/change it? 20 | const powerlimit = 1000 21 | 22 | func power(a *apl.Apl, f, g apl.Value) apl.Function { 23 | derived := func(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 24 | f := f.(apl.Function) 25 | gn, isgf := g.(apl.Function) 26 | to := ToIndex(nil) 27 | if isgf == false { 28 | // RO g is not a function but an integer. 29 | nv, ok := to.To(a, g) 30 | if ok == false { 31 | return nil, fmt.Errorf("power: non-function RO must be an integer: %T", g) 32 | } 33 | n := int(nv.(apl.Int)) 34 | if n < 0 { 35 | return nil, fmt.Errorf("power: function inverse is not implemented") 36 | } else if n == 0 { 37 | return R, nil 38 | } 39 | var err error 40 | v := R 41 | for i := 0; i < n; i++ { 42 | v, err = f.Call(a, L, v) 43 | if err != nil { 44 | return nil, err 45 | } 46 | } 47 | return v.Copy(), nil 48 | } else { 49 | // RO g is a function. 50 | var err error 51 | var fR, v apl.Value 52 | r := R 53 | m := 0 54 | for { 55 | if m > powerlimit { 56 | return nil, fmt.Errorf("power: recusion limit exceeded") 57 | } 58 | m++ 59 | fR, err = f.Call(a, L, r) 60 | if err != nil { 61 | return nil, err 62 | } 63 | v, err = gn.Call(a, fR.Copy(), r) 64 | if err != nil { 65 | return nil, err 66 | } 67 | nv, ok := to.To(a, v.Copy()) 68 | if ok == false { 69 | return nil, fmt.Errorf("power: gY must be an integer: %T", v) 70 | } 71 | n := int(nv.(apl.Int)) 72 | 73 | r = fR.Copy() 74 | if n == 1 { 75 | return r, nil 76 | } 77 | 78 | } 79 | } 80 | } 81 | return function(derived) 82 | } 83 | -------------------------------------------------------------------------------- /apl/operators/register.go: -------------------------------------------------------------------------------- 1 | package operators 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strings" 7 | 8 | "github.com/ktye/iv/apl" 9 | ) 10 | 11 | // Register adds the operators in this package to a. 12 | func Register(a *apl.Apl) { 13 | for _, op := range operators { 14 | a.RegisterOperator(op.symbol, op) 15 | } 16 | } 17 | 18 | type operator struct { 19 | apl.Domain 20 | symbol string 21 | doc string 22 | derived func(*apl.Apl, apl.Value, apl.Value) apl.Function 23 | selection func(*apl.Apl, apl.Value, apl.Value, apl.Value, apl.Value) (apl.IntArray, error) 24 | } 25 | 26 | func (op operator) Doc() string { return op.doc } 27 | func (op operator) Derived(a *apl.Apl, LO, RO apl.Value) apl.Function { 28 | return op.derived(a, LO, RO) 29 | } 30 | func (op operator) Select(a *apl.Apl, L, LO, RO, R apl.Value) (apl.IntArray, error) { 31 | if op.selection == nil { 32 | return apl.IntArray{}, fmt.Errorf("operator %s cannot be used in selective assignment", op.symbol) 33 | } else { 34 | return op.selection(a, L, LO, RO, R) 35 | } 36 | } 37 | func (op operator) DyadicOp() bool { 38 | if ar, ok := op.Domain.(arity); ok { 39 | return ar.DyadicOp() 40 | } 41 | // Domain must start with MonadicOp or DyadicOp 42 | panic(op.symbol + ": operator Domain must start with MonadicOp or DyadicOp") 43 | } 44 | 45 | type arity interface { 46 | DyadicOp() bool 47 | } 48 | 49 | var operators []operator 50 | 51 | func register(op operator) { 52 | // Add source path to documentation. 53 | _, fn, line, _ := runtime.Caller(1) 54 | if idx := strings.Index(fn, "apl/operators"); idx != -1 { 55 | fn = fn[idx:] 56 | } 57 | op.doc += fmt.Sprintf("\t%s:%d", fn, line) 58 | 59 | operators = append(operators, op) 60 | } 61 | 62 | // primitive implements a SingleDomain, which tests against the prmitive symbol. 63 | type primitive string 64 | 65 | func (p primitive) To(a *apl.Apl, V apl.Value) (apl.Value, bool) { 66 | if pf, ok := V.(apl.Primitive); ok && pf == apl.Primitive(p) { 67 | return V, true 68 | } 69 | return V, false 70 | } 71 | func (p primitive) String(f apl.Format) string { return string(p) } 72 | 73 | // function is both a func and implements the apl.Function interface, 74 | // by calling itself. 75 | // It is used to wrap derived functions to satisfy apl.Function. 76 | type function func(*apl.Apl, apl.Value, apl.Value) (apl.Value, error) 77 | 78 | func (f function) Call(a *apl.Apl, l, r apl.Value) (apl.Value, error) { 79 | return f(a, l, r) 80 | } 81 | -------------------------------------------------------------------------------- /apl/operators/select.go: -------------------------------------------------------------------------------- 1 | package operators 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | // selection returns a derived selection function, given an operator function that creates a derived function. 11 | func selection(op func(*apl.Apl, apl.Value, apl.Value) apl.Function) func(*apl.Apl, apl.Value, apl.Value, apl.Value, apl.Value) (apl.IntArray, error) { 12 | derived := func(a *apl.Apl, L, LO, RO, R apl.Value) (apl.IntArray, error) { 13 | 14 | // Create the derived function. 15 | df := op(a, LO, RO) 16 | 17 | // Create an index array with the shape of R. 18 | var ai apl.IntArray 19 | ar, ok := R.(apl.Array) 20 | if ok == false { 21 | return ai, fmt.Errorf("cannot select from %T", R) 22 | } 23 | ai.Dims = apl.CopyShape(ar) 24 | ai.Ints = make([]int, apl.Prod(ai.Dims)) 25 | for i := range ai.Ints { 26 | ai.Ints[i] = i + 1 27 | } 28 | 29 | // Apply the selection function to the index array. 30 | v, err := df.Call(a, L, ai) 31 | if err != nil { 32 | return ai, err 33 | } 34 | 35 | to := ToIndexArray(nil) 36 | if av, ok := to.To(a, v); ok == false { 37 | return ai, fmt.Errorf("could not convert selection to index array: %T", v) 38 | } else { 39 | ai = av.(apl.IntArray) 40 | for i := range ai.Ints { 41 | ai.Ints[i]-- 42 | } 43 | return ai, nil 44 | } 45 | } 46 | return derived 47 | } 48 | 49 | // selectSimple returns a general selection function for selective assignment. 50 | // It creates an index array of the same shape of R and applies f to it. 51 | // It is used by replicate and expand which behave like primitive functions instead of operators. 52 | // They take only 2 arguments. 53 | func selectSimple(f func(*apl.Apl, apl.Value, apl.Value) (apl.Value, error)) func(*apl.Apl, apl.Value, apl.Value, apl.Value, apl.Value) (apl.IntArray, error) { 54 | return func(a *apl.Apl, dummyL, L apl.Value, dummyRO, R apl.Value) (apl.IntArray, error) { 55 | 56 | // Create an index array with the shape of R. 57 | var ai apl.IntArray 58 | ar, ok := R.(apl.Array) 59 | if ok == false { 60 | return ai, fmt.Errorf("cannot select from %T", R) 61 | } 62 | ai.Dims = apl.CopyShape(ar) 63 | ai.Ints = make([]int, apl.Prod(ai.Dims)) 64 | for i := range ai.Ints { 65 | ai.Ints[i] = i + 1 66 | } 67 | 68 | // Apply the selection function to it. 69 | v, err := f(a, L, ai) 70 | if err != nil { 71 | return ai, err 72 | } 73 | 74 | to := ToIndexArray(nil) 75 | if av, ok := to.To(a, v); ok == false { 76 | return ai, fmt.Errorf("could not convert selection to index array: %T", v) 77 | } else { 78 | // Fill elements will be reported as ¯1, which the assignment should ignore. 79 | ai = av.(apl.IntArray) 80 | for i := range ai.Ints { 81 | ai.Ints[i]-- 82 | } 83 | return ai, nil 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /apl/primitives/axis.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | // splitAxis returns ax.R and converts ax.A to []int taking account of index origin. 11 | // It R is not an axis it returns R and nil. 12 | func splitAxis(a *apl.Apl, R apl.Value) (apl.Value, []int, error) { 13 | ax, ok := R.(apl.Axis) 14 | if ok == false { 15 | return R, nil, nil 16 | } 17 | if _, ok := ax.A.(apl.EmptyArray); ok { 18 | return ax.R, nil, nil 19 | } 20 | to := domain.ToIndexArray(nil) 21 | X, ok := to.To(a, ax.A) 22 | if ok == false { 23 | return nil, nil, fmt.Errorf("axis is not an index array") 24 | } 25 | ar := X.(apl.IntArray) 26 | shape := ar.Shape() 27 | if len(shape) != 1 { 28 | return nil, nil, fmt.Errorf("axis has wrong shape: %d", len(shape)) 29 | } 30 | x := make([]int, len(ar.Ints)) 31 | for i, n := range ar.Ints { 32 | x[i] = int(n - a.Origin) 33 | } 34 | return ax.R, x, nil 35 | } 36 | 37 | // SplitCatAxis splits the right argument, if it contains an axis. 38 | // The axis must be a numeric scalar value or a single element array. 39 | // It may contain a fractional part. 40 | // If it does not exist, it is set to the last axis. 41 | // It returns the integer part of the axis and indicates if it is fractional. 42 | // The index origin is substracted. 43 | func splitCatAxis(a *apl.Apl, L, R apl.Value) (apl.Value, int, bool, error) { 44 | ax, ok := R.(apl.Axis) 45 | if ok == false { 46 | if ar, ok := R.(apl.Array); ok == false { 47 | return R, 0, false, nil 48 | } else { 49 | return R, len(ar.Shape()) - 1, false, nil 50 | } 51 | } 52 | R = ax.R 53 | 54 | // X∊⍳(⍴⍴L)⌈⍴⍴R 55 | rkL := 0 56 | al, ok := L.(apl.Array) 57 | if ok { 58 | rkL = len(al.Shape()) 59 | } 60 | 61 | rkR := 0 62 | ar, ok := R.(apl.Array) 63 | if ok { 64 | rkR = len(ar.Shape()) 65 | } 66 | 67 | max := rkL 68 | if rkR > max { 69 | max = rkR 70 | } 71 | 72 | var x apl.Value 73 | if xr, ok := ax.A.(apl.Array); ok { 74 | if xr.Size() != 1 { 75 | return nil, 0, false, fmt.Errorf(",: axis must be a scalar or single element array") 76 | } else { 77 | x = xr.At(0) 78 | } 79 | } else { 80 | x = ax.A 81 | } 82 | num, ok := x.(apl.Number) 83 | if ok == false { 84 | return nil, 0, false, fmt.Errorf("axis is not numeric") 85 | } 86 | if n, ok := num.ToIndex(); ok { 87 | n -= a.Origin 88 | if n < 0 || n >= max { 89 | return nil, 0, false, fmt.Errorf("axis is out of range") 90 | } 91 | return R, n, false, nil 92 | } 93 | 94 | // The axis is fractional, depending on the numerical tower. 95 | // Substract index origin from the axis. 96 | n := 0 97 | if fl, ok := num.(floorer); ok == false { 98 | return nil, 0, false, fmt.Errorf("cannot floor axis: %T", num) 99 | } else { 100 | if fnum, ok := fl.Floor(); ok == false { 101 | return nil, 0, false, fmt.Errorf("could not floor axis: %T", num) 102 | } else if i, ok := fnum.(apl.Number).ToIndex(); ok == false { 103 | return nil, 0, false, fmt.Errorf("|axis is not an index") 104 | } else { 105 | n = int(i) 106 | } 107 | } 108 | 109 | // Substract index origin. 110 | n -= int(a.Origin) 111 | 112 | // x must be between -1 and max. 113 | if n < -1 || n > max { 114 | return nil, 0, false, fmt.Errorf("axis must be: X∊⍳(⍴⍴L)⌈⍴⍴R") 115 | } 116 | return R, n, true, nil 117 | } 118 | -------------------------------------------------------------------------------- /apl/primitives/boolean.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | "github.com/ktye/iv/apl/operators" 9 | ) 10 | 11 | func init() { 12 | tab := []struct { 13 | symbol string 14 | doc string 15 | logical string 16 | }{ 17 | {"^", "logical and", "and"}, 18 | {"∧", "logical and", "and"}, 19 | {"∨", "logical or", "or"}, 20 | {"⍲", "logical nand", "nand"}, 21 | {"⍱", "logical nor", "nor"}, 22 | } 23 | 24 | for _, e := range tab { 25 | register(primitive{ 26 | symbol: e.symbol, 27 | doc: e.doc, 28 | Domain: Dyadic(Split(IsScalar(nil), IsScalar(nil))), 29 | fn: arith2(e.symbol, logical(e.logical)), 30 | }) 31 | register(primitive{ 32 | symbol: e.symbol, 33 | doc: e.doc, 34 | Domain: arrays{}, 35 | fn: array2(e.symbol, logical(e.logical)), 36 | }) 37 | } 38 | register(primitive{ 39 | symbol: "~", 40 | doc: "logical not", 41 | Domain: Monadic(IsScalar(nil)), 42 | fn: arith1("~", logicalNot), 43 | }) 44 | register(primitive{ 45 | symbol: "~", 46 | doc: "logical not", 47 | Domain: Monadic(IsArray(nil)), 48 | fn: array1("~", logicalNot), 49 | }) 50 | register(primitive{ 51 | symbol: "~", 52 | doc: "without, excluding", 53 | Domain: Dyadic(Split(ToVector(nil), ToVector(nil))), 54 | fn: without, 55 | }) 56 | } 57 | 58 | // logical not, R is a Number. 59 | func logicalNot(a *apl.Apl, R apl.Value) (apl.Value, bool) { 60 | b, ok := a.Tower.ToBool(R.(apl.Number)) 61 | if ok == false { 62 | return nil, false 63 | } 64 | return apl.Bool(!b), true 65 | } 66 | 67 | func logical(logical string) func(*apl.Apl, apl.Value, apl.Value) (apl.Value, bool) { 68 | return func(a *apl.Apl, L, R apl.Value) (apl.Value, bool) { 69 | l, lok := a.Tower.ToBool(L.(apl.Number)) 70 | r, rok := a.Tower.ToBool(R.(apl.Number)) 71 | if lok == false || rok == false { 72 | if logical == "and" { 73 | return lcm(a, L, R) 74 | } else if logical == "or" { 75 | return gcd(a, L, R) 76 | } 77 | return nil, false 78 | } 79 | var t apl.Bool 80 | switch logical { 81 | case "and": 82 | t = l && r 83 | case "or": 84 | t = l || r 85 | case "nand": 86 | t = !(l && r) 87 | case "nor": 88 | t = !(l || r) 89 | default: 90 | panic(fmt.Sprintf("unknown logical: %s", logical)) 91 | } 92 | return t, true 93 | } 94 | } 95 | 96 | // without: L and R are vectors. 97 | // L~R is equivalent to (~L∊R)/L. 98 | func without(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 99 | if _, ok := R.(apl.EmptyArray); ok { 100 | return L, nil 101 | } 102 | if _, ok := L.(apl.EmptyArray); ok { 103 | return apl.EmptyArray{}, nil 104 | } 105 | 106 | lr, err := membership(a, L, R) 107 | if err != nil { 108 | return nil, err 109 | } 110 | 111 | not := arith1("~", logicalNot) 112 | if _, ok := L.(apl.Array); ok { 113 | not = array1("~", logicalNot) 114 | } 115 | nlr, err := not(a, nil, lr) 116 | if err != nil { 117 | return nil, err 118 | } 119 | 120 | to := ToIndexArray(nil) 121 | ia, ok := to.To(a, nlr) 122 | if ok == false { 123 | return nil, fmt.Errorf("without: cannot convert (~L∊R) to index array") 124 | } 125 | 126 | return operators.Replicate(a, ia, L, 0) 127 | } 128 | -------------------------------------------------------------------------------- /apl/primitives/channel.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/ktye/iv/apl" 8 | "github.com/ktye/iv/apl/numbers" 9 | ) 10 | 11 | // primitive < is defined in compare.go 12 | 13 | // channelSource sends any value R over a channel. 14 | // <[axis] R 15 | func channelSource(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 16 | r, ax, err := splitAxis(a, R) 17 | if err != nil { 18 | return nil, fmt.Errorf("channel send: %s", err) 19 | } 20 | if len(ax) > 1 { 21 | return nil, fmt.Errorf("channel send: axis must be scalar") 22 | } 23 | 24 | if _, ok := r.(apl.Channel); ok { 25 | return nil, fmt.Errorf("channel send: right argument is a channel") 26 | } 27 | 28 | n := 1 29 | if ax != nil { 30 | n = ax[0] 31 | } 32 | n += int(a.Origin) // splitAxis substracts the origin. 33 | 34 | c := apl.NewChannel() 35 | if n == 0 { 36 | // Send only once, but do not close any channels. 37 | go func(v apl.Value) { 38 | c[0] <- v 39 | }(r) 40 | return c, nil 41 | } 42 | 43 | // Send v n times. If n is negative send until c[1] is closed. 44 | go func(v apl.Value, n int) { 45 | defer close(c[0]) 46 | i := 0 47 | for { 48 | select { 49 | case _, ok := <-c[1]: 50 | if ok == false { 51 | return 52 | } 53 | case c[0] <- v: 54 | i++ 55 | if n > 0 && i >= n { 56 | return 57 | } 58 | } 59 | } 60 | }(r, n) 61 | return c, nil 62 | } 63 | 64 | // channelCopy connects two channels. It writes to L what it reads from R. 65 | // The function returns the number of values copied. 66 | func channelCopy(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 67 | l := L.(apl.Channel) 68 | r := R.(apl.Channel) 69 | // Should this run in a go-routine and return directly? 70 | defer close(l[0]) 71 | ret := apl.EmptyArray{} 72 | for { 73 | select { 74 | case _, ok := <-l[1]: 75 | if ok == false { 76 | close(r[1]) 77 | return ret, nil 78 | } 79 | case v, ok := <-r[0]: 80 | if ok == false { 81 | return ret, nil 82 | } 83 | select { 84 | case _, ok := <-l[1]: 85 | if ok == false { 86 | close(r[1]) 87 | return ret, nil 88 | } 89 | case l[0] <- v: 90 | } 91 | } 92 | } 93 | } 94 | 95 | // channelDelay returns a channel that sends at fixed intervals what it receives. 96 | func channelDelay(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 97 | d, ok := L.(numbers.Time).Duration() 98 | if ok == false { 99 | return nil, fmt.Errorf("channel delay: left argument is not a duration: %T", L) 100 | } 101 | in := R.(apl.Channel) 102 | out := in.Apply(a, Delay(d), nil, false) 103 | return out, nil 104 | } 105 | 106 | // Delay is a function that pauses execution for a given duration. 107 | // It is currently not bound to a primitive and only used by channel-delay. 108 | type Delay time.Duration 109 | 110 | func (d Delay) Call(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 111 | time.Sleep(time.Duration(d)) 112 | return R, nil 113 | } 114 | 115 | // channel1 applies the monadic elementary function to each value in a channel. 116 | func channel1(symbol string, fn func(*apl.Apl, apl.Value) (apl.Value, bool)) func(*apl.Apl, apl.Value, apl.Value) (apl.Value, error) { 117 | return func(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 118 | c := R.(apl.Channel) 119 | return c.Apply(a, apl.Primitive(symbol), nil, false), nil 120 | } 121 | } 122 | 123 | // channel2 applies the dyadic elementary function to each value in a channel. 124 | func channel2(symbol string, fn func(*apl.Apl, apl.Value, apl.Value) (apl.Value, bool)) func(*apl.Apl, apl.Value, apl.Value) (apl.Value, error) { 125 | return func(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 126 | c := R.(apl.Channel) 127 | return c.Apply(a, apl.Primitive(symbol), L, false), nil 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /apl/primitives/compare.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "reflect" 5 | "time" 6 | 7 | "github.com/ktye/iv/apl" 8 | . "github.com/ktye/iv/apl/domain" 9 | "github.com/ktye/iv/apl/numbers" 10 | ) 11 | 12 | func init() { 13 | tab := []struct { 14 | symbol, doc string 15 | }{ 16 | {"=", "equality"}, 17 | {"<", "less that"}, 18 | {">", "greater than"}, 19 | {"≠", "not equal"}, 20 | {"≤", "less or equal"}, 21 | {"≥", "greater or equal"}, 22 | } 23 | for _, e := range tab { 24 | register(primitive{ 25 | symbol: e.symbol, 26 | doc: e.doc, 27 | Domain: Dyadic(Split(IsScalar(nil), IsScalar(nil))), 28 | fn: arith2(e.symbol, compare(e.symbol)), 29 | }) 30 | register(primitive{ 31 | symbol: e.symbol, 32 | doc: e.doc, 33 | Domain: arrays{}, 34 | fn: array2(e.symbol, compare(e.symbol)), 35 | }) 36 | } 37 | 38 | register(primitive{ 39 | symbol: "<", 40 | doc: "channel send, source", 41 | Domain: Monadic(nil), 42 | fn: channelSource, // channel.go 43 | }) 44 | register(primitive{ 45 | symbol: "<", 46 | doc: "channel copy, connect", 47 | Domain: Dyadic(Split(IsChannel(nil), IsChannel(nil))), 48 | fn: channelCopy, // channel.go 49 | }) 50 | register(primitive{ 51 | symbol: "<", 52 | doc: "channel delay", 53 | Domain: Dyadic(Split(IsType(reflect.TypeOf(numbers.Time(time.Time{})), nil), IsChannel(nil))), 54 | fn: channelDelay, // channel.go 55 | }) 56 | } 57 | 58 | func compare(symbol string) func(*apl.Apl, apl.Value, apl.Value) (apl.Value, bool) { 59 | return func(a *apl.Apl, L apl.Value, R apl.Value) (apl.Value, bool) { 60 | switch symbol { 61 | case "=": 62 | return equals(L, R) 63 | case "<": 64 | return less(L, R) 65 | case ">": 66 | eq, ls, ok := equalless(L, R) 67 | if ok == false { 68 | return nil, false 69 | } 70 | return apl.Bool(!eq && !ls), true 71 | case "≠": 72 | eq, ok := equals(L, R) 73 | if ok == false { 74 | return nil, false 75 | } 76 | return apl.Bool(!eq), true 77 | case "≤": 78 | eq, ls, ok := equalless(L, R) 79 | if ok == false { 80 | return nil, false 81 | } 82 | return apl.Bool(eq || ls), true 83 | case "≥": 84 | eq, ls, ok := equalless(L, R) 85 | if ok == false { 86 | return nil, false 87 | } 88 | return apl.Bool(eq || !ls), true 89 | } 90 | return nil, false 91 | } 92 | } 93 | 94 | func equalless(L, R apl.Value) (apl.Bool, apl.Bool, bool) { 95 | eq, ok := equals(L, R) 96 | if ok == false { 97 | return false, false, false 98 | } 99 | ls, ok := less(L, R) 100 | if ok == false { 101 | return false, false, false 102 | } 103 | return eq, ls, true 104 | } 105 | func equals(L, R apl.Value) (apl.Bool, bool) { 106 | if eq, ok := L.(equaler); ok { 107 | return eq.Equals(R) 108 | } 109 | return apl.Bool(L == R), true 110 | } 111 | 112 | type equaler interface { 113 | Equals(apl.Value) (apl.Bool, bool) 114 | } 115 | 116 | func less(L, R apl.Value) (apl.Bool, bool) { 117 | if ls, ok := L.(lesser); ok { 118 | return ls.Less(R) 119 | } 120 | return false, false 121 | } 122 | 123 | type lesser interface { 124 | Less(apl.Value) (apl.Bool, bool) 125 | } 126 | -------------------------------------------------------------------------------- /apl/primitives/dict.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | "github.com/ktye/iv/apl/xgo" 9 | ) 10 | 11 | func init() { 12 | register(primitive{ 13 | symbol: "#", 14 | doc: "keys, methods", 15 | Domain: Monadic(nil), 16 | fn: keys, 17 | }) 18 | register(primitive{ 19 | symbol: "#", 20 | doc: "dict", 21 | Domain: Dyadic(nil), 22 | fn: dict, 23 | }) 24 | } 25 | 26 | // keys: R: object 27 | func keys(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 28 | methods := false 29 | if _, ok := R.(apl.Axis); ok { 30 | methods = true 31 | if r, _, err := splitAxis(a, R); err != nil { 32 | return nil, err 33 | } else { 34 | R = r 35 | } 36 | } 37 | obj, ok := R.(apl.Object) 38 | if ok == false { 39 | return nil, fmt.Errorf("keys: expected object: %T", R) 40 | } 41 | if methods { 42 | o, ok := obj.(xgo.Value) 43 | if ok == false { 44 | return nil, fmt.Errorf("methods: expected xgo.Value: %T", obj) 45 | } 46 | s := o.Methods() 47 | if s == nil { 48 | return apl.EmptyArray{}, nil 49 | } 50 | return apl.StringArray{Dims: []int{len(s)}, Strings: s}, nil 51 | } else { 52 | keyval := obj.Keys() 53 | values := make([]apl.Value, len(keyval)) 54 | for i, v := range keyval { 55 | values[i] = v.Copy() 56 | } 57 | return a.UnifyArray(apl.MixedArray{ 58 | Dims: []int{len(values)}, 59 | Values: values, 60 | }), nil 61 | } 62 | } 63 | 64 | func dict(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 65 | al, ok := L.(apl.Array) 66 | 67 | if ok == false { 68 | return &apl.Dict{ 69 | K: []apl.Value{L.Copy()}, 70 | M: map[apl.Value]apl.Value{ 71 | L: R.Copy(), 72 | }, 73 | }, nil 74 | } 75 | 76 | ls := al.Shape() 77 | if len(ls) != 1 { 78 | return nil, fmt.Errorf("dict: left argument must be a vector") 79 | } 80 | 81 | ar, ok := R.(apl.Array) 82 | if ok == false { 83 | mr := apl.NewMixed([]int{ls[0]}) 84 | for i := range mr.Values { 85 | mr.Values[i] = R.Copy() 86 | } 87 | ar = mr 88 | } 89 | rs := ar.Shape() 90 | if len(rs) != 1 || rs[0] != ls[0] { 91 | return nil, fmt.Errorf("dict: left and right arguments do not conform") 92 | } 93 | 94 | k := make([]apl.Value, al.Size()) 95 | m := make(map[apl.Value]apl.Value) 96 | for i := 0; i < al.Size(); i++ { 97 | l := al.At(i).Copy() 98 | m[l] = ar.At(i).Copy() 99 | k[i] = l 100 | } 101 | return &apl.Dict{ 102 | K: k, 103 | M: m, 104 | }, nil 105 | } 106 | -------------------------------------------------------------------------------- /apl/primitives/doc_test.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "testing" 7 | "text/tabwriter" 8 | 9 | "github.com/ktye/iv/apl" 10 | "github.com/ktye/iv/apl/numbers" 11 | "github.com/ktye/iv/apl/operators" 12 | aplstrings "github.com/ktye/iv/apl/strings" 13 | ) 14 | 15 | func TestDoc(t *testing.T) { 16 | if testing.Short() { 17 | a := apl.New(os.Stdout) 18 | numbers.Register(a) 19 | Register(a) 20 | operators.Register(a) 21 | aplstrings.Register(a, "s") 22 | 23 | var w io.Writer = os.Stdout 24 | tw := tabwriter.NewWriter(w, 2, 0, 2, ' ', 0) 25 | a.Doc(tw) 26 | tw.Flush() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apl/primitives/enclose.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | func init() { 11 | register(primitive{ 12 | symbol: "⊂", 13 | doc: "enclose, string catenation", 14 | Domain: Monadic(strvec{}), 15 | fn: strcat, 16 | }) 17 | register(primitive{ 18 | symbol: "⊂", 19 | doc: "join strings", 20 | Domain: Dyadic(Split(IsString(nil), strvec{})), 21 | fn: strjoin, 22 | }) 23 | register(primitive{ 24 | symbol: "⊃", 25 | doc: "split runes", 26 | Domain: Monadic(IsString(nil)), 27 | fn: runesplit, 28 | }) 29 | register(primitive{ 30 | symbol: "⊃", 31 | doc: "first", 32 | Domain: Monadic(IsList(nil)), 33 | fn: first, 34 | }) 35 | register(primitive{ 36 | symbol: "⊃", 37 | doc: "split string", 38 | Domain: Dyadic(Split(IsString(nil), IsString(nil))), 39 | fn: strsplit, 40 | }) 41 | } 42 | 43 | // strvec accepts an array if all elements are strings. 44 | // The result is a string vector. 45 | type strvec struct{} 46 | 47 | func (s strvec) To(a *apl.Apl, v apl.Value) (apl.Value, bool) { 48 | ar, ok := v.(apl.Array) 49 | if ok == false { 50 | return v, false 51 | } 52 | vec := apl.StringArray{Dims: []int{ar.Size()}, Strings: make([]string, ar.Size())} 53 | for i := 0; i < vec.Size(); i++ { 54 | if s, ok := ar.At(i).(apl.String); ok { 55 | vec.Strings[i] = string(s) 56 | } else { 57 | return v, false 58 | } 59 | } 60 | return vec, true 61 | } 62 | func (s strvec) String(f apl.Format) string { 63 | return "array of strings" 64 | } 65 | 66 | func strcat(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 67 | ar := R.(apl.StringArray) 68 | return apl.String(strings.Join(ar.Strings, "")), nil 69 | } 70 | 71 | func strjoin(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 72 | v := R.(apl.StringArray) 73 | return apl.String(strings.Join(v.Strings, string(L.(apl.String)))), nil 74 | } 75 | 76 | func runesplit(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 77 | r := []rune(string(R.(apl.String))) 78 | v := make([]string, len(r)) 79 | for i, s := range r { 80 | v[i] = string(s) 81 | } 82 | return apl.StringArray{Dims: []int{len(v)}, Strings: v}, nil 83 | } 84 | 85 | // split a string to an string vector 86 | // If L is a string, use it as the separator for strings.Split 87 | // If L is "", use strings.Field 88 | func strsplit(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 89 | l := L.(apl.String) 90 | r := R.(apl.String) 91 | var v []string 92 | if l == "" { 93 | v = strings.Fields(string(r)) 94 | } else { 95 | v = strings.Split(string(r), string(l)) 96 | } 97 | return apl.StringArray{Dims: []int{len(v)}, Strings: v}, nil 98 | } 99 | 100 | func first(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 101 | r := R.(apl.List) 102 | if len(r) == 0 { 103 | return apl.EmptyArray{}, nil 104 | } 105 | return r[0], nil 106 | } 107 | -------------------------------------------------------------------------------- /apl/primitives/find.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "github.com/ktye/iv/apl" 5 | . "github.com/ktye/iv/apl/domain" 6 | ) 7 | 8 | func init() { 9 | register(primitive{ 10 | symbol: "⍷", 11 | doc: "find", 12 | Domain: Dyadic(Split(ToArray(nil), ToArray(nil))), 13 | fn: find, 14 | }) 15 | } 16 | 17 | func find(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 18 | if _, ok := R.(apl.EmptyArray); ok { 19 | return apl.EmptyArray{}, nil 20 | } 21 | 22 | var al apl.Array 23 | el, Lempty := L.(apl.EmptyArray) 24 | if Lempty == false { 25 | al = L.(apl.Array) 26 | } else { 27 | al = el 28 | } 29 | ls := al.Shape() 30 | 31 | ar := R.(apl.Array) 32 | rs := ar.Shape() 33 | 34 | res := apl.BoolArray{Dims: apl.CopyShape(ar)} 35 | res.Bools = make([]bool, apl.Prod(res.Dims)) 36 | 37 | // If the rank of L is arger than the rank of R, nothing is found. 38 | if len(ls) > len(rs) { 39 | return res, nil 40 | } 41 | 42 | // If the rank of L is smaller than the rank of R, fill it with ones 43 | // at the beginning. 44 | if d := len(rs) - len(ls); d > 0 { 45 | shape := apl.CopyShape(ar) 46 | for i := range shape { 47 | if i < d { 48 | shape[i] = 1 49 | } else { 50 | shape[i] = ls[i-d] 51 | } 52 | } 53 | l := apl.MakeArray(al, shape) 54 | for i := 0; i < l.Size(); i++ { 55 | l.Set(i, al.At(i).Copy()) 56 | } 57 | al = l 58 | ls = shape 59 | } 60 | nl := al.Size() 61 | 62 | feq := arith2("=", compare("=")) 63 | ic, idx := apl.NewIdxConverter(rs) 64 | for i := range res.Bools { 65 | if nl > len(res.Bools)-i { 66 | res.Bools[i] = false 67 | } else { 68 | iseq := true 69 | for k := 0; k < len(idx); k++ { 70 | idx[k] = 0 71 | } 72 | for k := 0; k < nl; k++ { 73 | eq, err := feq(a, al.At(k), ar.At(i+ic.Index(idx))) 74 | if err != nil { 75 | return nil, err 76 | } 77 | if eq.(apl.Bool) == false { 78 | iseq = false 79 | break 80 | } 81 | apl.IncArrayIndex(idx, ls) 82 | } 83 | if iseq { 84 | res.Bools[i] = true 85 | } 86 | } 87 | } 88 | return res, nil 89 | } 90 | -------------------------------------------------------------------------------- /apl/primitives/gen.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "bufio" 7 | "fmt" 8 | "log" 9 | "os" 10 | "os/exec" 11 | "regexp" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | var prefix = regexp.MustCompile(` *apl_test.go:[0-9]*:`) 17 | var spaces = regexp.MustCompile(`^ *`) 18 | 19 | // This program is run by go generate. 20 | // It runs go test -v in short mode, which only includes 21 | // tests with the normal numeric tower. 22 | // The output of go test is filtered and written to Tests.md. 23 | func main() { 24 | mktest() 25 | mkref() 26 | } 27 | 28 | func mktest() { 29 | 30 | cmd := exec.Command("go", "test", "-v", "-short", "-run", "Normal") 31 | testout, err := cmd.StdoutPipe() 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | if err := cmd.Start(); err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | w, err := os.Create("../../TESTS.md") 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | defer w.Close() 44 | 45 | scn := bufio.NewScanner(testout) 46 | for scn.Scan() { 47 | s := scn.Text() 48 | if strings.HasPrefix(s, "===") { 49 | continue 50 | } 51 | if strings.HasPrefix(s, "---") { 52 | continue 53 | } 54 | s = prefix.ReplaceAllString(s, "") 55 | s = spaces.ReplaceAllString(s, "") 56 | fmt.Fprintln(w, s) 57 | } 58 | 59 | if err := cmd.Wait(); err != nil { 60 | log.Fatal(err) 61 | } 62 | fmt.Fprintf(w, "```\n") 63 | } 64 | 65 | func mkref() { 66 | cmd := exec.Command("go", "test", "-v", "-short", "-run", "Doc") 67 | testout, err := cmd.StdoutPipe() 68 | if err != nil { 69 | log.Fatal(err) 70 | } 71 | if err := cmd.Start(); err != nil { 72 | log.Fatal(err) 73 | } 74 | 75 | w, err := os.Create("../../REF.md") 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | defer w.Close() 80 | 81 | fmt.Fprintln(w, `# Reference 82 | - [Primitive Functions](#primitive-functions) 83 | - [Operators](#operators) 84 | `) 85 | 86 | scn := bufio.NewScanner(testout) 87 | for scn.Scan() { 88 | s := scn.Text() 89 | if strings.HasPrefix(s, "===") { 90 | continue 91 | } 92 | if strings.HasPrefix(s, "---") { 93 | continue 94 | } 95 | fmt.Fprintln(w, s) 96 | } 97 | 98 | if err := cmd.Wait(); err != nil { 99 | log.Fatal(err) 100 | } 101 | fmt.Fprintf(w, "\ngenerated by `go generate (apl/primitives/gen.go)` %s\n", time.Now().Format("2006-01-02 15:04:05")) 102 | } 103 | -------------------------------------------------------------------------------- /apl/primitives/match.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | func init() { 11 | register(primitive{ 12 | symbol: "≡", 13 | doc: "depth, level of nesting", 14 | Domain: Monadic(nil), 15 | fn: depth, 16 | }) 17 | register(primitive{ 18 | symbol: "≢", 19 | doc: "tally, number of major cells", 20 | Domain: Monadic(nil), 21 | fn: tally, 22 | }) 23 | 24 | register(primitive{ 25 | symbol: "≡", 26 | doc: "match", 27 | Domain: Dyadic(nil), 28 | fn: match, 29 | }) 30 | register(primitive{ 31 | symbol: "≢", 32 | doc: "not match", 33 | Domain: Dyadic(nil), 34 | fn: notmatch, 35 | }) 36 | } 37 | 38 | // depth reports the level of nesting. 39 | // Nested arrays are not supported, so depth is always 1 for arrays and 0 for scalars. 40 | func depth(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 41 | if l, ok := R.(apl.List); ok { 42 | return apl.Int(l.Depth()), nil 43 | } 44 | if _, ok := R.(apl.Array); ok { 45 | return apl.Int(1), nil 46 | } 47 | return apl.Int(0), nil 48 | } 49 | 50 | // tally returns the number of major cells of R. 51 | // It is equlivalent to {⍬⍴(⍴⍵),1}. 52 | func tally(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 53 | if t, ok := R.(apl.Table); ok { 54 | return apl.Int(t.Rows), nil 55 | } 56 | if o, ok := R.(apl.Object); ok { 57 | return apl.Int(len(o.Keys())), nil 58 | } 59 | ar, ok := R.(apl.Array) 60 | if ok == false { 61 | return apl.Int(1), nil 62 | } 63 | shape := ar.Shape() 64 | if len(shape) == 0 { 65 | return apl.Int(0), nil 66 | } 67 | return apl.Int(shape[0]), nil 68 | } 69 | 70 | func match(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 71 | al, isal := L.(apl.Array) 72 | ar, isar := R.(apl.Array) 73 | if isal != isar { 74 | return apl.Bool(false), nil 75 | } 76 | if isal == false { 77 | // Compare scalars, convert numbers to the same type. 78 | return apl.Bool(isEqual(a, L, R)), nil 79 | } else { 80 | sl := al.Shape() 81 | sr := ar.Shape() 82 | if len(sr) != len(sl) { 83 | return apl.Bool(false), nil 84 | } else if len(sr) == 0 { 85 | // Empty arrays must have the same type. 86 | if reflect.TypeOf(ar) == reflect.TypeOf(al) { 87 | return apl.Bool(true), nil 88 | } else { 89 | return apl.Bool(false), nil 90 | } 91 | } 92 | for i := range sl { 93 | if sl[i] != sr[i] { 94 | return apl.Bool(false), nil 95 | } 96 | } 97 | feq := arith2("=", compare("=")) 98 | for i := 0; i < ar.Size(); i++ { 99 | if iseq, err := feq(a, ar.At(i), al.At(i)); err != nil { 100 | return nil, err 101 | } else if iseq.(apl.Bool) == false { 102 | return apl.Bool(false), nil 103 | } 104 | } 105 | return apl.Bool(true), nil 106 | } 107 | } 108 | 109 | func notmatch(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 110 | if eq, err := match(a, L, R); err != nil { 111 | return nil, err 112 | } else { 113 | return !(eq.(apl.Bool)), nil 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /apl/primitives/query.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | 7 | "github.com/ktye/iv/apl" 8 | . "github.com/ktye/iv/apl/domain" 9 | "github.com/ktye/iv/apl/numbers" 10 | ) 11 | 12 | func init() { 13 | register(primitive{ 14 | symbol: "?", 15 | doc: "roll, rand, randn, bi-randn", 16 | Domain: Monadic(nil), 17 | fn: roll, 18 | }) 19 | register(primitive{ 20 | symbol: "?", 21 | doc: "deal", 22 | Domain: Dyadic(Split(ToScalar(ToIndex(nil)), ToScalar(ToIndex(nil)))), 23 | fn: deal, 24 | }) 25 | } 26 | 27 | // roll returns a number or an array of the same shape as R. 28 | // Values of R must be numbers 29 | func roll(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 30 | ar, ok := R.(apl.Array) 31 | if ok == false { 32 | if z, ok := R.(apl.Number); ok == false { 33 | return nil, fmt.Errorf("roll expectes a numeric array as the right argument") 34 | } else { 35 | if n, err := rollNumber(a, z); err != nil { 36 | return nil, err 37 | } else { 38 | return n, nil 39 | } 40 | } 41 | } 42 | if ar.Size() == 0 { 43 | return apl.EmptyArray{}, nil 44 | } 45 | res := apl.NewMixed(apl.CopyShape(ar)) 46 | for i := range res.Values { 47 | if z, ok := ar.At(i).(apl.Number); ok == false { 48 | return nil, fmt.Errorf("roll: array value is not numeric") 49 | } else { 50 | if n, err := rollNumber(a, z); err != nil { 51 | return nil, err 52 | } else { 53 | res.Values[i] = n 54 | } 55 | } 56 | } 57 | return a.UnifyArray(res), nil 58 | } 59 | 60 | // rollNumber returns a random integer upto n, which must be integer. 61 | // If n is 0, it returns a random float between 0 and 1. 62 | // If n is negative it returns a random number from a normal distribution with std←|n. 63 | // If n is complex, it returns a random number from a bivariate normal distribution 64 | // with normal parameters given by the real an imag part. 65 | // 66 | // TODO: seed. Currently random numbers are not seeded (equivalent to Seed(1)) 67 | // and always return the same values. 68 | func rollNumber(a *apl.Apl, n apl.Number) (apl.Number, error) { 69 | if f, ok := n.(numbers.Float); ok && float64(f) < 0 { 70 | return numbers.Float(float64(f) * rand.NormFloat64()), nil 71 | } 72 | if z, ok := n.(numbers.Complex); ok { 73 | return numbers.Complex(complex(real(z)*rand.NormFloat64(), imag(z)*rand.NormFloat64())), nil 74 | } 75 | m, ok := n.ToIndex() 76 | if ok == false || m < 0 { 77 | return numbers.Float(float64(m) * rand.NormFloat64()), nil 78 | } 79 | if m == 0 { 80 | // TODO: should we exclude 0? 81 | f := rand.Float64() 82 | return numbers.Float(f), nil // This only works with the default tower. 83 | } else { 84 | return a.Tower.Import(apl.Int(rand.Intn(m) + a.Origin)), nil 85 | } 86 | } 87 | 88 | // deal selects L random numbers from ⍳R without repetition. 89 | func deal(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 90 | // TODO []RL (random link) 91 | n := int(L.(apl.Int)) 92 | m := int(R.(apl.Int)) 93 | if n <= 0 || m < n { 94 | return nil, fmt.Errorf("deal: L must be > 0 and R >= L") 95 | } 96 | p := rand.Perm(m) 97 | p = p[:n] 98 | for i := range p { 99 | p[i] += a.Origin 100 | } 101 | return apl.IntArray{ 102 | Ints: p, 103 | Dims: []int{n}, 104 | }, nil 105 | } 106 | -------------------------------------------------------------------------------- /apl/primitives/register.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strings" 7 | 8 | "github.com/ktye/iv/apl" 9 | ) 10 | 11 | func Register(a *apl.Apl) { 12 | for _, p := range primitives { 13 | a.RegisterPrimitive(apl.Primitive(p.symbol), p) 14 | } 15 | } 16 | 17 | var primitives []primitive 18 | 19 | func register(p primitive) { 20 | // Add source path to documentation. 21 | _, fn, line, _ := runtime.Caller(1) 22 | if idx := strings.Index(fn, "apl/primitives"); idx != -1 { 23 | fn = fn[idx:] 24 | } 25 | p.doc += fmt.Sprintf("\t%s:%d", fn, line) 26 | 27 | primitives = append(primitives, p) 28 | } 29 | 30 | type primitive struct { 31 | apl.Domain 32 | symbol string 33 | doc string 34 | fn func(*apl.Apl, apl.Value, apl.Value) (apl.Value, error) 35 | sel func(*apl.Apl, apl.Value, apl.Value) (apl.IntArray, error) 36 | } 37 | 38 | func (p primitive) Call(a *apl.Apl, L, R apl.Value) (apl.Value, error) { return p.fn(a, L, R) } 39 | func (p primitive) Select(a *apl.Apl, L, R apl.Value) (apl.IntArray, error) { 40 | if p.sel == nil { 41 | return apl.IntArray{}, fmt.Errorf("primitive %s cannot be used in selective assignment", p.symbol) 42 | } 43 | return p.sel(a, L, R) 44 | } 45 | func (p primitive) Doc() string { return p.doc } 46 | -------------------------------------------------------------------------------- /apl/primitives/rho.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | . "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | func init() { 11 | register(primitive{ 12 | symbol: "⍴", 13 | doc: "shape", 14 | Domain: Monadic(nil), 15 | fn: rho1, 16 | }) 17 | register(primitive{ 18 | symbol: "⍴", 19 | doc: "reshape", 20 | Domain: Dyadic(Split(ToVector(ToIndexArray(nil)), ToArray(nil))), 21 | fn: rho2, 22 | sel: selection(rho2), 23 | }) 24 | register(primitive{ 25 | symbol: "⍴", 26 | doc: "reshape channel", 27 | Domain: Dyadic(Split(ToVector(ToIndexArray(nil)), IsChannel(nil))), 28 | fn: rhoChannel, 29 | }) 30 | } 31 | 32 | // Rho1 returns the shape of R. 33 | func rho1(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 34 | // Report a table as a two dimensional array. 35 | if t, ok := R.(apl.Table); ok == true { 36 | return apl.IntArray{ 37 | Dims: []int{2}, 38 | Ints: []int{t.Rows, len(t.K)}, 39 | }, nil 40 | } 41 | // An object returns the number of keys. 42 | if o, ok := R.(apl.Object); ok == true { 43 | n := len(o.Keys()) 44 | return apl.IntArray{Dims: []int{1}, Ints: []int{n}}, nil 45 | } 46 | 47 | if _, ok := R.(apl.Array); ok == false { 48 | return apl.EmptyArray{}, nil 49 | } 50 | // Shape of an empty array is 0, rank is 1 51 | if _, ok := R.(apl.EmptyArray); ok { 52 | return apl.IntArray{Ints: []int{0}, Dims: []int{1}}, nil 53 | } 54 | ar := R.(apl.Array) 55 | shape := ar.Shape() 56 | ret := apl.IntArray{ 57 | Ints: make([]int, len(shape)), 58 | Dims: []int{len(shape)}, 59 | } 60 | copy(ret.Ints, shape) 61 | return ret, nil 62 | } 63 | 64 | // Rho2 is dyadic reshape, L is empty or index array, R is array. 65 | func rho2(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 66 | // L is empty, returns empty. 67 | if L.(apl.Array).Size() == 0 { 68 | return apl.EmptyArray{}, nil 69 | } 70 | 71 | if _, ok := R.(apl.Object); ok { 72 | return nil, fmt.Errorf("cannot reshape %T", R) 73 | } 74 | 75 | l := L.(apl.IntArray) 76 | shape := make([]int, len(l.Ints)) 77 | copy(shape, l.Ints) 78 | if rs, ok := R.(apl.Reshaper); ok { 79 | return rs.Reshape(shape), nil 80 | } 81 | return nil, fmt.Errorf("cannot reshape %T", R) 82 | } 83 | 84 | // rhoChannel returns a channel and sends arrays with the shape of L. 85 | // Values are read from a channel R. 86 | func rhoChannel(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 87 | if L.(apl.Array).Size() == 0 { 88 | return R, nil 89 | } 90 | al := L.(apl.IntArray) 91 | size := apl.Prod(al.Ints) 92 | newarray := func() apl.MixedArray { 93 | s := make([]int, len(al.Ints)) 94 | copy(s, al.Ints) 95 | return apl.NewMixed(s) 96 | } 97 | res := newarray() 98 | 99 | in := R.(apl.Channel) 100 | out := apl.NewChannel() 101 | go func() { 102 | p := 0 103 | defer close(out[0]) 104 | push := func(v apl.Value) { 105 | res.Values[p] = v 106 | p++ 107 | if p == size { 108 | select { 109 | case _, ok := <-out[1]: 110 | if ok == false { 111 | close(in[1]) 112 | return 113 | } 114 | case out[0] <- res: 115 | res = newarray() 116 | p = 0 117 | } 118 | } 119 | } 120 | for { 121 | select { 122 | case _, ok := <-out[1]: 123 | if ok == false { 124 | close(in[1]) 125 | return 126 | } 127 | case v, ok := <-in[0]: 128 | if ok == false { 129 | return 130 | } 131 | if ar, ok := v.(apl.Array); ok { 132 | for i := 0; i < ar.Size(); i++ { 133 | push(ar.At(i)) 134 | } 135 | } else { 136 | push(v) 137 | } 138 | } 139 | } 140 | }() 141 | return out, nil 142 | } 143 | -------------------------------------------------------------------------------- /apl/primitives/selection.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | "github.com/ktye/iv/apl/domain" 8 | ) 9 | 10 | // Selection returns a general selection function for selective assignment. 11 | // It creates an index array of the same shape of R and applies f to it. 12 | func selection(f func(*apl.Apl, apl.Value, apl.Value) (apl.Value, error)) func(*apl.Apl, apl.Value, apl.Value) (apl.IntArray, error) { 13 | return func(a *apl.Apl, L apl.Value, R apl.Value) (apl.IntArray, error) { 14 | // Create an index array with the shape of R. 15 | var ai apl.IntArray 16 | ar, ok := R.(apl.Array) 17 | if ok == false { 18 | return ai, fmt.Errorf("cannot select from %T", R) 19 | } 20 | ai.Dims = apl.CopyShape(ar) 21 | ai.Ints = make([]int, ar.Size()) 22 | for i := range ai.Ints { 23 | ai.Ints[i] = i 24 | } 25 | 26 | // Apply the selection function to it. 27 | v, err := f(a, L, ai) 28 | if err != nil { 29 | return ai, err 30 | } 31 | 32 | to := domain.ToIndexArray(nil) 33 | if av, ok := to.To(a, v); ok == false { 34 | return ai, fmt.Errorf("could not convert selection to index array: %T", v) 35 | } else { 36 | return av.(apl.IntArray), nil 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /apl/primitives/tack.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "github.com/ktye/iv/apl" 5 | . "github.com/ktye/iv/apl/domain" 6 | ) 7 | 8 | func init() { 9 | register(primitive{ 10 | symbol: "⊣", 11 | doc: "left tack, same", 12 | Domain: Monadic(nil), 13 | fn: same, 14 | }) 15 | register(primitive{ 16 | symbol: "⊢", 17 | doc: "right tack, same", 18 | Domain: Monadic(nil), 19 | fn: same, 20 | }) 21 | register(primitive{ 22 | symbol: "⊣", 23 | doc: "left tack, left argument", 24 | Domain: Dyadic(nil), 25 | fn: left, 26 | }) 27 | register(primitive{ 28 | symbol: "⊢", 29 | doc: "right tack, right argument", 30 | Domain: Dyadic(nil), 31 | fn: right, 32 | }) 33 | } 34 | 35 | func same(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 36 | return R, nil 37 | } 38 | func left(a *apl.Apl, L, _ apl.Value) (apl.Value, error) { 39 | return L, nil 40 | } 41 | func right(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 42 | return R, nil 43 | } 44 | -------------------------------------------------------------------------------- /apl/primitives/type.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/ktye/iv/apl" 8 | . "github.com/ktye/iv/apl/domain" 9 | ) 10 | 11 | func init() { 12 | register(primitive{ 13 | symbol: "⌶", 14 | doc: "type", 15 | Domain: Monadic(nil), 16 | fn: typeof, 17 | }) 18 | register(primitive{ 19 | symbol: "⌶", 20 | doc: "convert to type", 21 | Domain: Dyadic(nil), 22 | fn: convert, 23 | }) 24 | register(primitive{ 25 | symbol: "⌶", 26 | doc: "convert to named type", 27 | Domain: Dyadic(Split(IsString(nil), nil)), 28 | fn: convert2, 29 | }) 30 | } 31 | 32 | func typeof(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 33 | return apl.String(reflect.TypeOf(R).String()), nil 34 | } 35 | 36 | // I-beam is a nice symbol for conversion, as it represents an encode-decode pair. 37 | 38 | // convert is called with a values of the destination type on the left. 39 | func convert(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 40 | // L can be a prototype, similar to the left argument of format. 41 | // TODO: use domain.ToType. 42 | return nil, fmt.Errorf("TODO: convert to destination type") 43 | } 44 | 45 | // convert is called with the name of the target type given as a string on the left argument. 46 | func convert2(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 47 | // TODO: L could be the string that monadic ibeam prints or 48 | // a prototype name similar to the left argument of format. 49 | s := L.(apl.String) 50 | switch s { 51 | case "img": 52 | to := ToImage(nil) 53 | if m, ok := to.To(a, R); ok { 54 | return m, nil 55 | } 56 | return nil, fmt.Errorf("cannot convert to image: %T", R) 57 | default: 58 | return nil, fmt.Errorf("convert: %T to %s is not supported", R, s) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /apl/primitives/unique.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "github.com/ktye/iv/apl" 5 | . "github.com/ktye/iv/apl/domain" 6 | ) 7 | 8 | func init() { 9 | register(primitive{ 10 | symbol: "∪", 11 | doc: "unique", 12 | Domain: Monadic(ToVector(nil)), 13 | fn: unique, 14 | }) 15 | register(primitive{ 16 | symbol: "∪", 17 | doc: "union", 18 | Domain: Dyadic(Split(ToVector(nil), ToVector(nil))), 19 | fn: union, 20 | }) 21 | } 22 | 23 | // unique: R is a vector. 24 | // DyRef gives an example of an array: ∪3 4 5⍴⍳20 25 | // which fails in tryapl. 26 | // ISO also allows only vectors. 27 | func unique(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 28 | ar := R.(apl.Array) 29 | 30 | var values []apl.Value 31 | for i := 0; i < ar.Size(); i++ { 32 | v := ar.At(i) 33 | u := true 34 | for k := range values { 35 | if isEqual(a, v, values[k]) { 36 | u = false 37 | break 38 | } 39 | } 40 | if u { 41 | values = append(values, v.Copy()) 42 | } 43 | } 44 | return a.UnifyArray(apl.MixedArray{Values: values, Dims: []int{len(values)}}), nil 45 | } 46 | 47 | // union of L and R, both are vectors. 48 | func union(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 49 | if _, ok := L.(apl.EmptyArray); ok { 50 | return R, nil 51 | } 52 | if _, ok := R.(apl.EmptyArray); ok { 53 | return L, nil 54 | } 55 | al := L.(apl.Array) 56 | ar := R.(apl.Array) 57 | 58 | var values []apl.Value 59 | appendvec := func(vec apl.Array) error { 60 | for i := 0; i < vec.Size(); i++ { 61 | v := vec.At(i) 62 | u := true 63 | for k := range values { 64 | if isEqual(a, v, values[k]) == true { 65 | u = false 66 | break 67 | } 68 | } 69 | if u { 70 | values = append(values, v.Copy()) 71 | } 72 | } 73 | return nil 74 | } 75 | if err := appendvec(al); err != nil { 76 | return nil, err 77 | } 78 | if err := appendvec(ar); err != nil { 79 | return nil, err 80 | } 81 | return a.UnifyArray(apl.MixedArray{Dims: []int{len(values)}, Values: values}), nil 82 | } 83 | -------------------------------------------------------------------------------- /apl/rpc/README.md: -------------------------------------------------------------------------------- 1 | # rpc package 2 | 3 | The package provides client and server interfaces for communication between distributed 4 | APL instances. 5 | 6 | ## Server 7 | An server could be implemented as: 8 | 9 | ```go 10 | package main 11 | 12 | import ( 13 | "github.com/ktye/iv/apl" 14 | "github.com/ktye/iv/apl/numbers" 15 | "github.com/ktye/iv/apl/operators" 16 | "github.com/ktye/iv/apl/primitives" 17 | "github.com/ktye/iv/apl/rpc" 18 | ) 19 | 20 | func main() { 21 | a := apl.New(nil) 22 | numbers.Register(a) 23 | primitives.Register(a) 24 | operators.Register(a) 25 | rpc.Register(a, "") 26 | 27 | rpc.ListenAndServe(a, ":1966") 28 | } 29 | 30 | ``` 31 | When running, it listens on port 1966 for connections. 32 | 33 | ## Client 34 | On a different process, run a normal APL session: 35 | 36 | ``` 37 | C←rpc→dial ":1966" 38 | rpc→call (C; "+/"; 5; ⍳10;) 39 | 15 20 25 30 35 40 40 | ``` 41 | 42 | The rpc call evaluates the function string in the remote environment 43 | and calls it with the local values on the remote process. 44 | 45 | APL value that should be transfered over the wire need to be 46 | registerd to the gob package. 47 | See `init.go` for values that are already registerd. 48 | -------------------------------------------------------------------------------- /apl/rpc/init.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "encoding/gob" 5 | 6 | "github.com/ktye/iv/apl" 7 | "github.com/ktye/iv/apl/numbers" 8 | ) 9 | 10 | func init() { 11 | // Register types for communication. 12 | gob.Register(apl.Bool(false)) 13 | gob.Register(apl.Int(0)) 14 | gob.Register(numbers.Float(0.0)) 15 | gob.Register(numbers.Complex(0)) 16 | gob.Register(apl.String("")) 17 | gob.Register(apl.List(nil)) 18 | gob.Register(apl.MixedArray{}) 19 | gob.Register(apl.IntArray{}) 20 | gob.Register(apl.Bool(false)) 21 | } 22 | -------------------------------------------------------------------------------- /apl/rpc/register.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ktye/iv/apl" 7 | ) 8 | 9 | // Register adds the rpc package to the interpreter. 10 | // See README.md 11 | func Register(a *apl.Apl, name string) { 12 | pkg := map[string]apl.Value{ 13 | "dial": apl.ToFunction(dial), 14 | "call": apl.ToFunction(call), 15 | "close": apl.ToFunction(closeconn), 16 | } 17 | if name == "" { 18 | name = "rpc" 19 | } 20 | a.RegisterPackage(name, pkg) 21 | } 22 | 23 | func dial(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 24 | if L != nil { 25 | return nil, fmt.Errorf("rpc dial must be called monadically") 26 | } 27 | if s, ok := R.(apl.String); ok == false { 28 | return nil, fmt.Errorf("rpc dial: argument must be a string") 29 | } else { 30 | return Dial(string(s)) 31 | } 32 | } 33 | 34 | func call(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 35 | if L != nil { 36 | return nil, fmt.Errorf("rpc call must be called monadically") 37 | } 38 | lst, ok := R.(apl.List) 39 | if ok == false { 40 | return nil, fmt.Errorf("rpc call: argument must be a list: %T", R) 41 | } 42 | if len(lst) < 3 { 43 | return nil, fmt.Errorf("rpc call: argument list is too short") 44 | } 45 | if len(lst) > 4 { 46 | return nil, fmt.Errorf("rpc call: argument list is too long") 47 | } 48 | c, ok := lst[0].(Conn) 49 | if ok == false { 50 | return nil, fmt.Errorf("rpc call: first list argument must be a connection") 51 | } 52 | f, ok := lst[1].(apl.String) 53 | if ok == false { 54 | return nil, fmt.Errorf("rpc call: second list argument must be a string") 55 | } 56 | 57 | var Larg, Rarg apl.Value 58 | if len(lst) == 3 { 59 | Rarg = lst[2] 60 | } else { 61 | Larg = lst[2] 62 | Rarg = lst[3] 63 | } 64 | return c.Call(string(f), Larg, Rarg) 65 | } 66 | 67 | func closeconn(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 68 | c, ok := R.(Conn) 69 | if ok == false { 70 | return nil, fmt.Errorf("right argument must be a connection") 71 | } 72 | return c.Close() 73 | } 74 | -------------------------------------------------------------------------------- /apl/rpc/rpc.go: -------------------------------------------------------------------------------- 1 | // Package rpc adds remove procedure calls to APL. 2 | package rpc 3 | 4 | import ( 5 | "encoding/gob" 6 | "fmt" 7 | "net" 8 | 9 | "github.com/ktye/iv/apl" 10 | ) 11 | 12 | func Dial(address string) (Conn, error) { 13 | c, err := net.Dial("tcp", address) 14 | if err != nil { 15 | return Conn{}, err 16 | } 17 | return Conn{Conn: c}, nil 18 | } 19 | 20 | type Conn struct { 21 | net.Conn 22 | } 23 | 24 | func (c Conn) String(f apl.Format) string { 25 | remote := c.RemoteAddr() 26 | if remote == nil { 27 | return fmt.Sprintf("rpc→conn not connected") 28 | } 29 | return fmt.Sprintf("rpc→conn to %s", remote.String()) 30 | } 31 | func (c Conn) Copy() apl.Value { return c } 32 | 33 | func (c Conn) Close() (apl.Value, error) { 34 | if c.Conn == nil { 35 | return nil, fmt.Errorf("not connected") 36 | } 37 | if err := c.Conn.Close(); err != nil { 38 | return nil, err 39 | } 40 | return apl.Int(1), nil 41 | } 42 | 43 | func (c Conn) Call(f string, L, R apl.Value) (apl.Value, error) { 44 | req := Request{Fn: f, L: L, R: R} 45 | if err := gob.NewEncoder(c.Conn).Encode(req); err != nil { 46 | c.Conn.Close() 47 | return nil, err 48 | } 49 | var res Response 50 | if err := gob.NewDecoder(c.Conn).Decode(&res); err != nil { 51 | c.Conn.Close() 52 | return nil, err 53 | } 54 | if res.Err != "" { 55 | return nil, fmt.Errorf("%s", res.Err) 56 | } else if res.V == nil { 57 | return nil, fmt.Errorf("empty result") 58 | } 59 | return res.V, nil 60 | } 61 | -------------------------------------------------------------------------------- /apl/rpc/server.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "encoding/gob" 5 | "fmt" 6 | "log" 7 | "net" 8 | 9 | "github.com/ktye/iv/apl" 10 | ) 11 | 12 | // ListenAndServe puts APL into server mode. 13 | // It accepts a single connection at a time from anyone. 14 | func ListenAndServe(a *apl.Apl, addr string) { 15 | ln, err := net.Listen("tcp", addr) 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | defer ln.Close() 20 | 21 | log.Print("listen on ", addr) 22 | for { 23 | conn, err := ln.Accept() 24 | if err != nil { 25 | log.Print(err) 26 | } else { 27 | handle(a, conn) 28 | } 29 | } 30 | } 31 | 32 | type Request struct { 33 | Fn string 34 | L, R apl.Value 35 | } 36 | 37 | type Response struct { 38 | Err string 39 | V apl.Value 40 | } 41 | 42 | func handle(a *apl.Apl, cn net.Conn) { 43 | log.Print("conn ", cn.RemoteAddr()) 44 | var req Request 45 | for { 46 | var res Response 47 | if err := gob.NewDecoder(cn).Decode(&req); err != nil { 48 | res.Err = err.Error() 49 | } else { 50 | if v, err := exec(a, req); err != nil { 51 | res.Err = err.Error() 52 | } else { 53 | res.V = v 54 | } 55 | } 56 | if err := gob.NewEncoder(cn).Encode(res); err != nil { 57 | log.Print(err) 58 | cn.Close() 59 | return 60 | } 61 | } 62 | } 63 | 64 | func exec(a *apl.Apl, req Request) (apl.Value, error) { 65 | if req.R == nil { 66 | return nil, fmt.Errorf("right argument is nil") 67 | } 68 | if p, err := a.Parse(req.Fn); err != nil { 69 | return nil, err 70 | } else if len(p) != 1 { 71 | return nil, fmt.Errorf("expected a single function expression: got %d", len(p)) 72 | } else if v, err := p[0].Eval(a); err != nil { 73 | return nil, err 74 | } else if f, ok := v.(apl.Function); ok == false { 75 | return nil, fmt.Errorf("expr is not a function") 76 | } else { 77 | return f.Call(a, req.L, req.R) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /apl/string.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "reflect" 8 | ) 9 | 10 | type String string 11 | 12 | // String prints a string. 13 | func (s String) String(f Format) string { 14 | fs := f.Fmt[reflect.TypeOf(s)] 15 | if fs == "" { 16 | if f.PP < 0 { 17 | fs = "%q" 18 | } 19 | } 20 | if fs == "" { 21 | return string(s) 22 | } 23 | return fmt.Sprintf(fs, string(s)) 24 | } 25 | 26 | func (s String) Copy() Value { 27 | return s 28 | } 29 | 30 | func (s String) Eval(a *Apl) (Value, error) { 31 | return s, nil 32 | } 33 | 34 | func (s String) ReadFrom(a *Apl, r io.Reader) (Value, error) { 35 | b, err := ioutil.ReadAll(r) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return String(b), nil 40 | } 41 | 42 | // Less implements primitives.lesser to be used for comparison and sorting. 43 | func (s String) Less(r Value) (Bool, bool) { 44 | b, ok := r.(String) 45 | if ok == false { 46 | return false, false 47 | } 48 | return s < b, true 49 | } 50 | 51 | func (s String) Export() reflect.Value { 52 | return reflect.ValueOf(string(s)) 53 | } 54 | 55 | // StringArray is a uniform array of strings. 56 | type StringArray struct { 57 | Dims []int 58 | Strings []string 59 | } 60 | 61 | func (s StringArray) String(f Format) string { 62 | return ArrayString(f, s) 63 | } 64 | 65 | func (s StringArray) Copy() Value { 66 | r := StringArray{Dims: CopyShape(s), Strings: make([]string, len(s.Strings))} 67 | copy(r.Strings, s.Strings) 68 | return r 69 | } 70 | 71 | func (s StringArray) At(i int) Value { 72 | return String(s.Strings[i]) 73 | } 74 | 75 | func (s StringArray) Shape() []int { 76 | return s.Dims 77 | } 78 | 79 | func (s StringArray) Size() int { 80 | return len(s.Strings) 81 | } 82 | 83 | func (s StringArray) Zero() Value { 84 | return String("") 85 | } 86 | 87 | func (s StringArray) Set(i int, v Value) error { 88 | if i < 0 || i > len(s.Strings) { 89 | return fmt.Errorf("index out of range") 90 | } 91 | if c, ok := v.(String); ok { 92 | s.Strings[i] = string(c) 93 | return nil 94 | } 95 | return fmt.Errorf("cannot assign %T to StringArray", v) 96 | } 97 | 98 | func (s StringArray) Make(shape []int) Uniform { 99 | return StringArray{ 100 | Dims: shape, 101 | Strings: make([]string, Prod(shape)), 102 | } 103 | } 104 | 105 | func makeStringArray(v []Value) StringArray { 106 | str := make([]string, len(v)) 107 | for i, e := range v { 108 | str[i] = string(e.(String)) 109 | } 110 | return StringArray{ 111 | Dims: []int{len(v)}, 112 | Strings: str, 113 | } 114 | } 115 | 116 | func (s StringArray) Reshape(shape []int) Value { 117 | res := StringArray{ 118 | Dims: shape, 119 | Strings: make([]string, Prod(shape)), 120 | } 121 | k := 0 122 | for i := range res.Strings { 123 | res.Strings[i] = s.Strings[k] 124 | k++ 125 | if k == len(s.Strings) { 126 | k = 0 127 | } 128 | } 129 | return res 130 | } 131 | -------------------------------------------------------------------------------- /apl/strings/register.go: -------------------------------------------------------------------------------- 1 | // Package strings provides go string functions 2 | package strings 3 | 4 | import ( 5 | "reflect" 6 | "strings" 7 | 8 | "github.com/ktye/iv/apl" 9 | "github.com/ktye/iv/apl/xgo" 10 | ) 11 | 12 | func Register(a *apl.Apl, name string) { 13 | if name == "" { 14 | name = "s" 15 | } 16 | pkg := map[string]apl.Value{ 17 | "compare": xgo.Function{Name: "Compare", Fn: reflect.ValueOf(strings.Compare)}, 18 | "contains": xgo.Function{Name: "Contains", Fn: reflect.ValueOf(strings.Contains)}, 19 | "containsany": xgo.Function{Name: "ContainsAny", Fn: reflect.ValueOf(strings.ContainsAny)}, 20 | "containsrune": xgo.Function{Name: "ContainsRune", Fn: reflect.ValueOf(strings.ContainsRune)}, 21 | "count": xgo.Function{Name: "Count", Fn: reflect.ValueOf(strings.Count)}, 22 | "equalfold": xgo.Function{Name: "EqualFold", Fn: reflect.ValueOf(strings.EqualFold)}, 23 | "fields": xgo.Function{Name: "Fields", Fn: reflect.ValueOf(strings.Fields)}, 24 | "fieldsfunc": xgo.Function{Name: "FieldsFunc", Fn: reflect.ValueOf(strings.FieldsFunc)}, 25 | "hasprefix": xgo.Function{Name: "HasPrefix", Fn: reflect.ValueOf(strings.HasPrefix)}, 26 | "hassuffix": xgo.Function{Name: "HasSuffix", Fn: reflect.ValueOf(strings.HasSuffix)}, 27 | "index": xgo.Function{Name: "Index", Fn: reflect.ValueOf(strings.Index)}, 28 | "indexany": xgo.Function{Name: "IndexAny", Fn: reflect.ValueOf(strings.IndexAny)}, 29 | "indexbyte": xgo.Function{Name: "IndexByte", Fn: reflect.ValueOf(strings.IndexByte)}, 30 | "indexfunc": xgo.Function{Name: "IndexFunc", Fn: reflect.ValueOf(strings.IndexFunc)}, 31 | "indexrune": xgo.Function{Name: "IndexRune", Fn: reflect.ValueOf(strings.IndexRune)}, 32 | "join": xgo.Function{Name: "Join", Fn: reflect.ValueOf(strings.Join)}, 33 | "lastindex": xgo.Function{Name: "LastIndex", Fn: reflect.ValueOf(strings.LastIndex)}, 34 | "lastindexany": xgo.Function{Name: "LastIndexAny", Fn: reflect.ValueOf(strings.LastIndexAny)}, 35 | "lastindexbyte": xgo.Function{Name: "LastIndexByte", Fn: reflect.ValueOf(strings.LastIndexByte)}, 36 | "repeat": xgo.Function{Name: "Repeat", Fn: reflect.ValueOf(strings.Repeat)}, 37 | "replace": xgo.Function{Name: "Replace", Fn: reflect.ValueOf(strings.Replace)}, 38 | "split": xgo.Function{Name: "Split", Fn: reflect.ValueOf(strings.Split)}, 39 | "splitafter": xgo.Function{Name: "SplitAfter", Fn: reflect.ValueOf(strings.SplitAfter)}, 40 | "splitaftern": xgo.Function{Name: "SplitAfterN", Fn: reflect.ValueOf(strings.SplitAfterN)}, 41 | "splitn": xgo.Function{Name: "SplitN", Fn: reflect.ValueOf(strings.SplitN)}, 42 | "title": xgo.Function{Name: "Title", Fn: reflect.ValueOf(strings.Title)}, 43 | "tolower": xgo.Function{Name: "ToLower", Fn: reflect.ValueOf(strings.ToLower)}, 44 | "tolowerspecial": xgo.Function{Name: "ToLowerSpecial", Fn: reflect.ValueOf(strings.ToLowerSpecial)}, 45 | "totitle": xgo.Function{Name: "ToTitle", Fn: reflect.ValueOf(strings.ToTitle)}, 46 | "totitlespecial": xgo.Function{Name: "ToTitleSpecial", Fn: reflect.ValueOf(strings.ToTitleSpecial)}, 47 | "toupper": xgo.Function{Name: "ToUpper", Fn: reflect.ValueOf(strings.ToUpper)}, 48 | "toupperspecial": xgo.Function{Name: "ToUpperSpecial", Fn: reflect.ValueOf(strings.ToUpperSpecial)}, 49 | "trim": xgo.Function{Name: "Trim", Fn: reflect.ValueOf(strings.Trim)}, 50 | "trimleft": xgo.Function{Name: "TrimLeft", Fn: reflect.ValueOf(strings.TrimLeft)}, 51 | "trimprefix": xgo.Function{Name: "TrimPrefix", Fn: reflect.ValueOf(strings.TrimPrefix)}, 52 | "trimright": xgo.Function{Name: "TrimRight", Fn: reflect.ValueOf(strings.TrimRight)}, 53 | "trimspace": xgo.Function{Name: "TrimSpace", Fn: reflect.ValueOf(strings.TrimSpace)}, 54 | "trimsuffix": xgo.Function{Name: "TrimSuffix", Fn: reflect.ValueOf(strings.TrimSuffix)}, 55 | } 56 | a.RegisterPackage(name, pkg) 57 | } 58 | -------------------------------------------------------------------------------- /apl/train.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Train is a function train expression. 9 | // It does not contain it's arguments like a Primitive or a fnVar. 10 | // See DyaProg p 25. 11 | type train []expr 12 | 13 | func (t train) Eval(a *Apl) (Value, error) { 14 | return t, nil 15 | } 16 | 17 | func (t train) String(f Format) string { 18 | v := make([]string, len(t)) 19 | for i := range t { 20 | v[i] = t[i].String(f) 21 | } 22 | return "(" + strings.Join(v, ", ") + ")" 23 | } 24 | func (t train) Copy() Value { return t } 25 | 26 | func (t train) Call(a *Apl, L, R Value) (Value, error) { 27 | if len(t) < 2 { 28 | return nil, fmt.Errorf("cannot call short train, length %d", len(t)) 29 | } else if len(t)%2 == 0 { 30 | // even number: f g h i j k → f(g h(i j k)) ⍝ atop(fork(fork)) 31 | f := atop{} 32 | end := 1 33 | if len(t) == 2 { 34 | end = 2 35 | } 36 | for i, e := range t[:end] { 37 | if v, err := e.Eval(a); err != nil { 38 | return nil, err 39 | } else { 40 | f[i] = v 41 | } 42 | } 43 | if len(t) > 3 { 44 | f[1] = train(t[1:]) 45 | } 46 | return f.Call(a, L, R) 47 | } else { 48 | // odd number: e f g h i j k → e f(g h(i j k)) ⍝ fork(fork(fork)) 49 | f := fork{} 50 | end := 2 51 | if len(t) == 3 { 52 | end = 3 53 | } 54 | for i, e := range t[:end] { 55 | if v, err := e.Eval(a); err != nil { 56 | return nil, err 57 | } else { 58 | f[i] = v 59 | } 60 | } 61 | if len(t) > 3 { 62 | f[2] = train(t[2:]) 63 | } 64 | return f.Call(a, L, R) 65 | } 66 | } 67 | 68 | type atop [2]Value 69 | 70 | func (t atop) String(f Format) string { 71 | return fmt.Sprintf("(%s %s)", t[0].String(f), t[1].String(f)) 72 | } 73 | 74 | func (t atop) Call(a *Apl, L, R Value) (Value, error) { 75 | g, ok := t[0].(Function) 76 | if ok == false { 77 | return nil, fmt.Errorf("atop: expected function g: %T", t[0]) 78 | } 79 | h, ok := t[1].(Function) 80 | if ok == false { 81 | return nil, fmt.Errorf("atop: expected function h: %T", t[1]) 82 | } 83 | 84 | // L may be nil. 85 | v, err := h.Call(a, L, R) 86 | if err != nil { 87 | return nil, err 88 | } 89 | return g.Call(a, nil, v) 90 | } 91 | 92 | type fork [3]Value 93 | 94 | func (fk fork) String(f Format) string { 95 | return fmt.Sprintf("(%s %s %s)", fk[0].String(f), fk[1].String(f), fk[2].String(f)) 96 | } 97 | 98 | func (fk fork) Call(a *Apl, L, R Value) (Value, error) { 99 | f, fok := fk[0].(Function) 100 | 101 | g, ok := fk[1].(Function) 102 | if ok == false { 103 | return nil, fmt.Errorf("fork: expected function g: %T", fk[1]) 104 | } 105 | h, ok := fk[2].(Function) 106 | if ok == false { 107 | return nil, fmt.Errorf("fork: expected function h: %T", fk[2]) 108 | } 109 | 110 | // Agh fork if f is not a function. 111 | var l Value 112 | l = fk[0] 113 | if fok { 114 | if v, err := f.Call(a, L, R); err != nil { // TODO copy? 115 | return nil, err 116 | } else { 117 | l = v 118 | } 119 | } 120 | r, err := h.Call(a, L, R) // TODO copy? 121 | if err != nil { 122 | return nil, err 123 | } 124 | return g.Call(a, l, r) 125 | } 126 | -------------------------------------------------------------------------------- /apl/uniform.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // Unify tries to convert the array to a uniform array, if possible. 9 | // If uptype is true, it uptypes numeric values, if that helps. 10 | func (a *Apl) Unify(A Array, uptype bool) (resultarray Array, resultok bool) { 11 | if _, ok := A.(EmptyArray); ok { 12 | return A, false // An empty array is defined to be not uniform. 13 | } 14 | if _, ok := A.(Uniform); ok { 15 | return A, true 16 | } 17 | 18 | noNumber := -10 19 | boolType := reflect.TypeOf(Bool(false)) 20 | indexType := reflect.TypeOf(Int(0)) 21 | class := func(t reflect.Type) int { 22 | n, ok := a.Tower.Numbers[t] 23 | if ok == false { 24 | if t == indexType { 25 | return -1 26 | } else if t == boolType { 27 | return -2 28 | } 29 | return noNumber 30 | } 31 | return n.Class 32 | } 33 | 34 | // If all values of the array are the same type, it is uniform. 35 | // This includes a List who's primary values are lists as well. 36 | size := A.Size() 37 | if size < 1 { 38 | return A, true 39 | } 40 | v0 := A.At(0) 41 | t0 := reflect.TypeOf(v0) 42 | max := class(t0) 43 | var maxnumber Number 44 | if max != noNumber { 45 | maxnumber = v0.(Number) 46 | } 47 | sametype := true 48 | for i := 1; i < size; i++ { 49 | v := A.At(i) 50 | t := reflect.TypeOf(v) 51 | if t != t0 { 52 | sametype = false 53 | if uptype == false { 54 | return A, false 55 | } 56 | } 57 | if max != noNumber { 58 | if c := class(t); c == noNumber { 59 | max = noNumber 60 | } else if c > max { 61 | max = c 62 | maxnumber = v.(Number) 63 | } 64 | } 65 | } 66 | 67 | // All values have the same type. 68 | // Try to convert them to a compact uniform type. 69 | if sametype { 70 | // Some uniform types are defined in the numeric implementation. 71 | // E.g. numbers/{FloatArray;ComplexArray;TimeArray}. 72 | if max != noNumber { 73 | var values []Value 74 | switch v := A.(type) { 75 | case MixedArray: 76 | values = v.Values 77 | case List: 78 | values = []Value(v) 79 | default: 80 | return A, true 81 | } 82 | if u, ok := a.Tower.Uniform(values); ok == false { 83 | return A, true 84 | } else if rs, ok := u.(Reshaper); ok { 85 | return rs.Reshape(CopyShape(A)).(Array), true 86 | } 87 | } 88 | // Some uniform types are defined in array.go. 89 | if t0 == reflect.TypeOf(String("")) { 90 | ar := StringArray{}.Make(CopyShape(A)) 91 | for i := 0; i < A.Size(); i++ { 92 | v := A.At(i) 93 | ar.(StringArray).Strings[i] = string(v.(String)) 94 | } 95 | return ar, true 96 | } else if t0 == reflect.TypeOf(Bool(false)) { 97 | ar := BoolArray{}.Make(CopyShape(A)) 98 | for i := 0; i < A.Size(); i++ { 99 | v := A.At(i) 100 | ar.(BoolArray).Bools[i] = bool(v.(Bool)) 101 | } 102 | return ar, true 103 | } else if t0 == reflect.TypeOf(Int(0)) { 104 | ar := IntArray{}.Make(CopyShape(A)) 105 | for i := 0; i < A.Size(); i++ { 106 | v := A.At(i) 107 | ar.(IntArray).Ints[i] = int(v.(Int)) 108 | } 109 | return ar, true 110 | } 111 | // Unknown uniform type is returnd as it is. 112 | return A, true 113 | } else if max == noNumber { 114 | // If values are not of the same type, and not identified by 115 | // the current tower, there is no chance to make them equal. 116 | return A, false 117 | } 118 | 119 | // Try to uptype all values to the same number type. 120 | values := make([]Value, size) 121 | var err error 122 | for i := 0; i < size; i++ { 123 | v := A.At(i) 124 | values[i], _, err = a.Tower.SameType(v.(Number), maxnumber) 125 | if err != nil { 126 | fmt.Println(err) 127 | return A, false 128 | } 129 | } 130 | if u, ok := a.Tower.Uniform(values); ok { 131 | if rs, ok := u.(Reshaper); ok { 132 | return rs.Reshape(CopyShape(A)).(Array), true 133 | } 134 | } 135 | return A, false 136 | } 137 | 138 | // UnifyArray tries to unify the input array without uptyping. 139 | func (a *Apl) UnifyArray(A Array) Array { 140 | u, _ := a.Unify(A, false) 141 | return u 142 | } 143 | -------------------------------------------------------------------------------- /apl/value.go: -------------------------------------------------------------------------------- 1 | package apl 2 | 3 | import "io" 4 | 5 | // Value is the result of an evaluation. 6 | // Any type that implements the interface is a valid type for apl. 7 | // 8 | // The String method is used to display the value. 9 | // It does not need to be unique or represent the input syntax. 10 | // Mosty types have no input respresentation at all. 11 | // They are the result of a computation. 12 | // 13 | // A Value may implement further interfaces such as Array, Uniform or Function. 14 | type Value interface { 15 | String(Format) string 16 | Copy() Value 17 | } 18 | 19 | // VarReader is implemented by Values that are able to parse from a Reader. 20 | // The ReadFrom method must return a new value of the same type. 21 | // The function should be able to parse the format of it's String method. 22 | // It is used by varfs in package io. 23 | type VarReader interface { 24 | ReadFrom(*Apl, io.Reader) (Value, error) 25 | } 26 | -------------------------------------------------------------------------------- /apl/xgo/convert.go: -------------------------------------------------------------------------------- 1 | package xgo 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/ktye/iv/apl" 8 | "github.com/ktye/iv/apl/numbers" 9 | ) 10 | 11 | // Exporter can be implemented by an apl.Value to be able convert it to a go value. 12 | type Exporter interface { 13 | Export() reflect.Value 14 | } 15 | 16 | // export converts an apl value to a go value. 17 | func export(v apl.Value, t reflect.Type) (reflect.Value, error) { 18 | 19 | if e, ok := v.(Exporter); ok { 20 | x := e.Export() 21 | if x.Type().ConvertibleTo(t) { 22 | return x.Convert(t), nil 23 | } 24 | } 25 | 26 | zero := reflect.Value{} 27 | switch t.Kind() { 28 | 29 | case reflect.Int: 30 | return reflect.ValueOf(int(v.(apl.Int))), nil 31 | 32 | case reflect.Float64: 33 | return reflect.ValueOf(float64(v.(numbers.Float))), nil 34 | 35 | case reflect.Complex128: 36 | return reflect.ValueOf(complex128(v.(numbers.Complex))), nil 37 | 38 | case reflect.String: 39 | return reflect.ValueOf(string(v.(apl.String))), nil 40 | 41 | case reflect.Slice: 42 | ar, ok := v.(apl.Array) 43 | if ok == false { 44 | return zero, fmt.Errorf("expected slice: %T", v) 45 | } 46 | et := t.Elem() 47 | n := ar.Size() 48 | s := reflect.MakeSlice(t, n, n) 49 | for i := 0; i < n; i++ { 50 | if e, err := export(ar.At(i), et); err != nil { 51 | return zero, err 52 | } else { 53 | se := s.Index(i) 54 | se.Set(e) 55 | } 56 | } 57 | return s, nil 58 | 59 | case reflect.Struct: 60 | if xv, ok := v.(Value); ok { 61 | st := reflect.Value(xv).Type() 62 | if st == reflect.PtrTo(t) { 63 | return reflect.Indirect(reflect.Value(xv)), nil 64 | } else if st == t { 65 | return reflect.Value(xv), nil 66 | } 67 | } 68 | return zero, fmt.Errorf("xgo: export struct: cannot convert %T to %s", v, t) 69 | 70 | default: 71 | return zero, fmt.Errorf("cannot convert to %v (%s)", t, t.Kind()) 72 | } 73 | } 74 | 75 | // convert converts a go value to an apl value. 76 | func Convert(v reflect.Value) (apl.Value, error) { 77 | switch v.Kind() { 78 | case reflect.Int: 79 | return apl.Int(int(v.Int())), nil 80 | 81 | case reflect.Uint: 82 | return apl.Int(int(v.Uint())), nil 83 | 84 | case reflect.Float64: 85 | return numbers.Float(v.Float()), nil 86 | 87 | case reflect.Complex128: 88 | return numbers.Complex(v.Complex()), nil 89 | 90 | case reflect.String: 91 | return apl.String(v.String()), nil 92 | 93 | case reflect.Slice: 94 | n := v.Len() 95 | ar := apl.NewMixed([]int{n}) 96 | for i := range ar.Values { 97 | if e, err := Convert(v.Index(i)); err != nil { 98 | return nil, err 99 | } else { 100 | ar.Values[i] = e 101 | } 102 | } 103 | return ar, nil 104 | 105 | case reflect.Struct: 106 | return Value(v.Addr()), nil // TODO: populate 107 | 108 | default: 109 | return nil, fmt.Errorf("cannot convert %s to an apl value", v.Kind()) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /apl/xgo/function.go: -------------------------------------------------------------------------------- 1 | package xgo 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/ktye/iv/apl" 8 | ) 9 | 10 | type Function struct { 11 | Name string 12 | Fn reflect.Value 13 | } 14 | 15 | func (f Function) String(af apl.Format) string { 16 | return f.Name 17 | } 18 | func (f Function) Copy() apl.Value { return f } 19 | 20 | // Call a go function. 21 | // If it requires 1 argument, that is taken from the right value. 22 | // Two arguments may be the right and left argument or a vector of 2 arguments. 23 | // More than two arguments must be passed in a vector of the right size. 24 | // If the function returns an error as the last value, it is checked and returned. 25 | // Otherwise, or if the error is nil the result is converted and returned. 26 | // More than one result will be returned as a List. 27 | func (f Function) Call(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 28 | errarg := func(i int, err error) error { 29 | return fmt.Errorf("function %s argument %d: %s", f.Name, i+1, err) 30 | } 31 | t := f.Fn.Type() 32 | args := t.NumIn() 33 | in := make([]reflect.Value, args) 34 | var err error 35 | if args == 0 { 36 | } else if args == 1 { 37 | in[0], err = export(R, t.In(0)) 38 | if err != nil { 39 | return nil, errarg(0, err) 40 | } 41 | } else if args == 2 && L != nil { 42 | in[0], err = export(R, t.In(0)) 43 | if err != nil { 44 | return nil, errarg(0, err) 45 | } 46 | in[1], err = export(L, t.In(1)) 47 | if err != nil { 48 | return nil, errarg(1, err) 49 | } 50 | } else if L == nil { 51 | ar, ok := R.(apl.Array) 52 | if ok == false { 53 | return nil, fmt.Errorf("function %s requires %d arguments", f.Name, args) 54 | } 55 | rs := ar.Shape() 56 | if len(rs) > 1 { 57 | return nil, fmt.Errorf("function argument has rank %d", len(rs)) 58 | } 59 | if n := ar.Size(); n != args { 60 | return nil, fmt.Errorf("function %s requires %d arguments, R has size %d", f.Name, args, n) 61 | } else { 62 | for i := 0; i < args; i++ { 63 | in[i], err = export(ar.At(i), t.In(i)) 64 | if err != nil { 65 | return nil, errarg(i, err) 66 | } 67 | } 68 | } 69 | } 70 | out := f.Fn.Call(in) 71 | 72 | // Test if the last output value is an error, check and remove it. 73 | if len(out) > 0 { 74 | if last := out[len(out)-1]; last.Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) { 75 | if last.IsNil() == false { 76 | return nil, last.Interface().(error) 77 | } else { 78 | out = out[:len(out)-1] 79 | } 80 | } 81 | } 82 | 83 | if len(out) == 0 { 84 | return apl.EmptyArray{}, nil 85 | } else if len(out) == 1 { 86 | return Convert(out[0]) 87 | } else { 88 | res := make(apl.List, len(out)) 89 | for i := range out { 90 | if v, err := Convert(out[i]); err != nil { 91 | return nil, err 92 | } else { 93 | res[i] = v 94 | } 95 | } 96 | return res, nil 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /apl/xgo/method.go: -------------------------------------------------------------------------------- 1 | package xgo 2 | 3 | /* TODO remove 4 | import ( 5 | "fmt" 6 | "reflect" 7 | 8 | "github.com/ktye/iv/apl" 9 | ) 10 | 11 | type Method struct { 12 | Value reflect.Value 13 | Method string 14 | } 15 | 16 | func (m Method) String(a *apl.Apl) { 17 | fmt.Srintf("%v→%s", m.Value.Type(), m.Method) 18 | } 19 | 20 | func (m Method) Call(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 21 | var zero reflect.Value 22 | if m.Value == zero { 23 | return nil, fmt.Errorf("method has no value") 24 | } 25 | fn := reflect.ValueOf(m.Value).MethodByName(m.Method) 26 | if fn == zero { 27 | return nil, fmt.Errorf("method %s does not exist", m.Method) 28 | } 29 | fn := Function{Name: m.Method, Fn: fn} 30 | return fn.Call(a, L, R) 31 | } 32 | */ 33 | -------------------------------------------------------------------------------- /apl/xgo/register.go: -------------------------------------------------------------------------------- 1 | package xgo 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | 8 | "github.com/ktye/iv/apl" 9 | ) 10 | 11 | func Register(a *apl.Apl, name string) { 12 | if name == "" { 13 | name = "go" 14 | } 15 | pkg := map[string]apl.Value{ 16 | "t": New(reflect.TypeOf(T{})), 17 | "s": New(reflect.TypeOf(S{})), 18 | "i": New(reflect.TypeOf(I(0))), 19 | "source": source{}, 20 | "echo": echo{}, 21 | } 22 | a.RegisterPackage("go", pkg) 23 | } 24 | 25 | type I int 26 | 27 | // T is an example struct with methods with pointer receivers. 28 | type T struct { 29 | A string 30 | I int 31 | F float64 32 | C complex128 33 | V []string 34 | S S 35 | } 36 | 37 | func (t *T) Inc() { 38 | t.I++ 39 | } 40 | 41 | func (t *T) Join(sep string) (int, string) { 42 | s := strings.Join(t.V, sep) 43 | return len(t.V), s 44 | } 45 | 46 | // S is an example struct with a method without pointer receiver. 47 | type S struct { 48 | A int 49 | B int 50 | V []int 51 | } 52 | 53 | func (s S) Sum() int { 54 | return s.A + s.B 55 | } 56 | 57 | // source returns a Channel to pull numbers from. 58 | // It stops if the max value is reached or the channel is closed. 59 | // It is used for demonstrating apl.Channel. 60 | type source struct{} 61 | 62 | func (_ source) String(f apl.Format) string { 63 | return "source" 64 | } 65 | func (s source) Copy() apl.Value { return s } 66 | 67 | func (s source) Call(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 68 | num, ok := R.(apl.Number) 69 | if ok == false { 70 | return nil, fmt.Errorf("source: r must be a number") 71 | } 72 | n, ok := num.ToIndex() 73 | if ok == false || n <= 0 { 74 | return nil, fmt.Errorf("source: R must be a positive integer") 75 | } 76 | c := apl.NewChannel() 77 | go func(c apl.Channel) { 78 | for i := 0; i < n; i++ { 79 | select { 80 | case <-c[1]: 81 | close(c[0]) 82 | return 83 | default: 84 | c[0] <- apl.Int(i) 85 | } 86 | } 87 | close(c[0]) 88 | }(c) 89 | return c, nil 90 | } 91 | 92 | // echo returns a Channel which echos what was send to it. 93 | type echo struct{} 94 | 95 | func (_ echo) String(f apl.Format) string { 96 | return "echo" 97 | } 98 | func (e echo) Copy() apl.Value { return e } 99 | 100 | func (_ echo) Call(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 101 | p := "" 102 | if s, ok := R.(apl.String); ok == false || len(s) == 0 { 103 | return nil, fmt.Errorf("echo: R must be a non-empty string") 104 | } else { 105 | p = string(s) 106 | } 107 | c := apl.NewChannel() 108 | go func(c apl.Channel) { 109 | var buf []apl.Value 110 | s := "empty stack" 111 | for { 112 | select { 113 | case r, ok := <-c[1]: 114 | if ok == false { 115 | close(c[0]) 116 | return 117 | } 118 | buf = append(buf, r) 119 | if len(buf) == 1 { 120 | s = p + r.String(a.Format) 121 | } 122 | case c[0] <- apl.String(s): 123 | if len(buf) > 1 { 124 | copy(buf, buf[1:]) 125 | buf = buf[:len(buf)-1] 126 | if len(buf) > 0 { 127 | s = p + buf[0].String(a.Format) 128 | } 129 | } else if len(buf) == 1 { 130 | buf = buf[:0] 131 | s = p + "empty stack" 132 | } 133 | } 134 | } 135 | close(c[0]) 136 | }(c) 137 | return c, nil 138 | } 139 | -------------------------------------------------------------------------------- /apl/xgo/type.go: -------------------------------------------------------------------------------- 1 | package xgo 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | "text/tabwriter" 8 | "unicode" 9 | 10 | "github.com/ktye/iv/apl" 11 | ) 12 | 13 | // New returns an initialization function for the given type. 14 | func New(t reflect.Type) create { 15 | return create{t} 16 | } 17 | 18 | type Value reflect.Value 19 | 20 | // xgo values are copied by reference. 21 | func (v Value) Copy() apl.Value { 22 | return v 23 | } 24 | 25 | func (v Value) String(f apl.Format) string { 26 | keys := v.Keys() 27 | if keys == nil { 28 | return fmt.Sprintf("xgo.Value (not a struct) %T", v) 29 | } 30 | var buf strings.Builder 31 | tw := tabwriter.NewWriter(&buf, 1, 0, 1, ' ', 0) 32 | for _, k := range keys { 33 | val := v.At(k) 34 | s := "" 35 | if val == nil { 36 | s = "?" 37 | } else { 38 | s = val.String(f) 39 | } 40 | fmt.Fprintf(tw, "%s:\t%s\n", k.String(f), s) 41 | } 42 | tw.Flush() 43 | s := buf.String() 44 | if len(s) > 0 && s[len(s)-1] == '\n' { 45 | return s[:len(s)-1] 46 | } 47 | return s 48 | } 49 | 50 | // Keys returns the field names, if the value is a struct. 51 | // It does not return the method names. 52 | // It returns nil, if the Value is not a struct. 53 | func (v Value) Keys() []apl.Value { 54 | val := reflect.Value(v) 55 | if val.Kind() == reflect.Ptr { 56 | val = val.Elem() 57 | } 58 | if val.Kind() != reflect.Struct { 59 | return nil 60 | } 61 | t := val.Type() 62 | n := t.NumField() 63 | res := make([]apl.Value, n) 64 | for i := 0; i < n; i++ { 65 | res[i] = apl.String(t.Field(i).Name) 66 | } 67 | return res 68 | } 69 | 70 | func (v Value) Methods() []string { 71 | val := reflect.Value(v) 72 | t := val.Type() 73 | n := t.NumMethod() 74 | res := make([]string, n) 75 | for i := range res { 76 | res[i] = lower(t.Method(i).Name) 77 | } 78 | return res 79 | } 80 | 81 | // Field returns the value of a field or a method with the given name. 82 | func (v Value) At(key apl.Value) apl.Value { 83 | name, ok := key.(apl.String) 84 | if ok == false { 85 | return nil 86 | } 87 | val := reflect.Value(v) 88 | var zero reflect.Value 89 | Name := upper(string(name)) 90 | m := val.MethodByName(Name) 91 | if m != zero { 92 | return Function{Name: Name, Fn: m} 93 | } 94 | if val.Kind() == reflect.Ptr { 95 | val = val.Elem() 96 | } 97 | if val.Kind() != reflect.Struct { 98 | return nil 99 | } 100 | sf := val.FieldByName(Name) 101 | if sf == zero { 102 | return nil 103 | } 104 | rv, err := Convert(sf) 105 | if err != nil { 106 | return nil 107 | } 108 | return rv 109 | } 110 | 111 | func (v Value) Set(key apl.Value, fv apl.Value) error { 112 | field, ok := key.(apl.String) 113 | if ok == false { 114 | return fmt.Errorf("key must be a string") 115 | } 116 | val := reflect.Value(v).Elem() 117 | if val.Kind() != reflect.Struct { 118 | return fmt.Errorf("not a struct: cannot set field") 119 | } 120 | sf := val.FieldByName(string(field)) 121 | var zero reflect.Value 122 | if sf == zero { 123 | return fmt.Errorf("%v: field does not exist: %s", val.Type(), field) 124 | } 125 | sv, err := export(fv, sf.Type()) 126 | if err != nil { 127 | return err 128 | } 129 | sf.Set(sv) 130 | return nil 131 | } 132 | 133 | type create struct { 134 | reflect.Type 135 | } 136 | 137 | func (t create) String(f apl.Format) string { 138 | return fmt.Sprintf("new %v", t.Type) 139 | } 140 | func (t create) Copy() apl.Value { 141 | return t 142 | } 143 | 144 | func (t create) Call(a *apl.Apl, L, R apl.Value) (apl.Value, error) { 145 | v := reflect.New(t.Type) 146 | return Value(v), nil 147 | } 148 | 149 | func upper(s string) string { 150 | return firstrune(s, unicode.ToUpper) 151 | } 152 | 153 | func lower(s string) string { 154 | return firstrune(s, unicode.ToLower) 155 | } 156 | 157 | func firstrune(s string, f func(r rune) rune) string { 158 | var buf strings.Builder 159 | for i, r := range s { 160 | if i == 0 { 161 | buf.WriteRune(f(r)) 162 | } else { 163 | buf.WriteRune(r) 164 | } 165 | } 166 | return buf.String() 167 | } 168 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | set -e 2 | cd cmd/iv && GOOS=linux GOARCH=amd64 go build -o ../../ivL main.go && cd - 3 | cd cmd/iv && GOOS=darwin GOARCH=amd64 go build -o ../../ivM main.go && cd - 4 | cd cmd/iv && GOOS=windows GOARCH=amd64 go build -o ../../iv.exe main.go && cd - 5 | cd cmd/apl && GOOS=linux GOARCH=amd64 go build -o ../../aplL main.go && cd - 6 | cd cmd/apl && GOOS=darwin GOARCH=amd64 go build -o ../../aplM main.go && cd - 7 | cd cmd/apl && GOOS=windows GOARCH=amd64 go build -o ../../apl.exe main.go && cd - 8 | -------------------------------------------------------------------------------- /cmd/apl.go: -------------------------------------------------------------------------------- 1 | // Package cmd contains shared code between cmd/apl, cmd/iv and cmd/lui 2 | package cmd 3 | 4 | import ( 5 | "bufio" 6 | "fmt" 7 | "io" 8 | "os" 9 | 10 | "github.com/ktye/iv/apl" 11 | ) 12 | 13 | // Apl runs the interpreter in file mode if arguments are given, otherwise as a repl. 14 | func Apl(a *apl.Apl, stdin io.Reader, args []string) error { 15 | // Execute files. 16 | if len(args) > 0 { 17 | for _, name := range args { 18 | var r io.Reader 19 | if name == "-" { 20 | r = stdin 21 | name = "stdin" 22 | } else { 23 | f, err := os.Open(name) 24 | if err != nil { 25 | return err 26 | } 27 | defer f.Close() 28 | r = f 29 | } 30 | return a.EvalFile(r, name) 31 | } 32 | return nil 33 | } 34 | 35 | // Run interactively. 36 | scanner := bufio.NewScanner(stdin) 37 | fmt.Printf(" ") 38 | for scanner.Scan() { 39 | s := scanner.Text() 40 | if err := a.ParseAndEval(s); err != nil { 41 | fmt.Println(err) 42 | } 43 | fmt.Printf(" ") 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /cmd/apl/README.md: -------------------------------------------------------------------------------- 1 | # cmd/apl 2 | 3 | Apl is a simple command line program that runs APL\iv. 4 | It includes only the basic packages *numbers*, *primitives* and *operators*. 5 | 6 | It is just one example to use the interpreter. 7 | A more advanced program is `cmd/lui`. 8 | 9 | ## Usage 10 | ``` 11 | apl 12 | ``` 13 | If no input argument is given, the program acts as a simple REPL reading a line at a time. 14 | Multiline statements are errors. 15 | On error it prints a message but continues. 16 | 17 | Apl does not contain a readline library. If you need line editing or history, consider `rlwrap`. 18 | 19 | ``` 20 | apl FILE ... 21 | ``` 22 | If one or more arguments are given, it reads input from these files. 23 | Multiline statements are allowed. 24 | On error it prints a message to stderr and exits. 25 | If an argument is `-` it reads from stdin, but otherwise behaves like reading from a file. 26 | 27 | 28 | ## Testing 29 | `go test` runs all file in `testdata/*.apl` and compares the results to the corresponding `.out` files. 30 | If a file is known to fail, it's error message is in a `.err` file. 31 | 32 | More unit tests are in `apl/primitives/apl_test.go`. 33 | 34 | ## Example 35 | ``` 36 | apl testdata/a.apl 37 | ``` 38 | -------------------------------------------------------------------------------- /cmd/apl/apl_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/ktye/iv/cmd" 8 | ) 9 | 10 | func init() { 11 | if err := os.Chdir("testdata"); err != nil { 12 | panic(err) 13 | } 14 | } 15 | 16 | func TestApl(t *testing.T) { 17 | if err := cmd.AplTest(newApl); err != nil { 18 | t.Fatal(err) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cmd/apl/main.go: -------------------------------------------------------------------------------- 1 | // APL interpreter. 2 | // 3 | // Usage 4 | // apl < INPUT 5 | // apl FILES... 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | 12 | "github.com/ktye/iv/apl" 13 | "github.com/ktye/iv/apl/big" 14 | "github.com/ktye/iv/apl/numbers" 15 | "github.com/ktye/iv/apl/operators" 16 | "github.com/ktye/iv/apl/primitives" 17 | "github.com/ktye/iv/cmd" 18 | ) 19 | 20 | func main() { 21 | a := newApl() 22 | a.SetOutput(os.Stdout) 23 | if err := cmd.Apl(a, os.Stdin, os.Args[1:]); err != nil { 24 | fmt.Fprintln(os.Stderr, err) 25 | os.Exit(1) 26 | } 27 | } 28 | 29 | func newApl() *apl.Apl { 30 | a := apl.New(nil) 31 | numbers.Register(a) 32 | big.Register(a, "") 33 | primitives.Register(a) 34 | operators.Register(a) 35 | return a 36 | } 37 | -------------------------------------------------------------------------------- /cmd/apl/testdata/a.apl: -------------------------------------------------------------------------------- 1 | 1+1 2 | 3 | ⍝ Multiline lambda expression 4 | f←{ 5 | ⍺<0: -⍵ 6 | ⍵ 7 | } 8 | 9 | 5 f 3 10 | (1-2)f 3 11 | -------------------------------------------------------------------------------- /cmd/apl/testdata/a.out: -------------------------------------------------------------------------------- 1 | 2 2 | 3 3 | ¯3 4 | -------------------------------------------------------------------------------- /cmd/apl/testdata/abc.apl: -------------------------------------------------------------------------------- 1 | big→set 1 2 | A←154476802108746166441951315019919837485664325669565431700026634898253202035277999 3 | B←36875131794129999827197811565225474825492979968971970996283137471637224634055579 4 | C←4373612677928697257861252602371390152816537558161613618621437993378423467772036 5 | 4=(A÷B+C)+(B÷A+C)+C÷A+B 6 | -------------------------------------------------------------------------------- /cmd/apl/testdata/abc.out: -------------------------------------------------------------------------------- 1 | 1 2 | 1 3 | -------------------------------------------------------------------------------- /cmd/apl/testdata/e.apl: -------------------------------------------------------------------------------- 1 | 1x 2 | -------------------------------------------------------------------------------- /cmd/apl/testdata/e.err: -------------------------------------------------------------------------------- 1 | e.apl:1: cannot parse number: 1x 2 | -------------------------------------------------------------------------------- /cmd/apl/testdata/finn.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktye/iv/5c1b6375ce570a8839f81d3a500be7f503ba1ce3/cmd/apl/testdata/finn.out -------------------------------------------------------------------------------- /cmd/apl/testdata/fmt.apl: -------------------------------------------------------------------------------- 1 | ⍝ Formatting examples for arrays, dicts and lists to csv, json, mat 2 | ¯1⍕2 3 4⍴⍳24 ⍝ display format 3 | ¯2⍕2 3 4⍴⍳24 ⍝ json 4 | ¯3⍕3⍴⍳3 ⍝ matlab vector 5 | ¯3⍕2 3⍴⍳6 ⍝ matlab vector 6 | ⍝ string arrays 7 | S←"abc" "def" "gh\ni" 8 | ¯1⍕3 2⍴S 9 | `json ⍕3 2⍴S 10 | `mat ⍕3 2⍴S 11 | ⍝ floats and complex 12 | S←1.2 3.4 5.6J¯7.8 13 | ¯1⍕3 2⍴S 14 | `csv ⍕3 2⍴S 15 | `json ⍕3 2⍴S 16 | `mat ⍕3 2⍴S 17 | ⍝ dictionaries 18 | S←`A`b`zeta#(1; "zwei";1 2 3;) 19 | S 20 | ¯1⍕S 21 | ¯2⍕S 22 | ¯3⍕S 23 | ⍝ lists 24 | L←(1 2;(3;(1;2;);"four";);5;) 25 | L 26 | ¯1⍕L 27 | ¯2⍕L 28 | ⍝ Numbers 29 | 123 0123 0x1234 30 | ¯1⍕123 0123 0x123 31 | ¯8⍕123 0123 0x123 32 | ¯16⍕123 0123 0x123 33 | `x ⍕123 0123 0x123 34 | `x ⍕ 1.23 35 | -------------------------------------------------------------------------------- /cmd/apl/testdata/fmt.out: -------------------------------------------------------------------------------- 1 | 1 2 3 4 2 | 5 6 7 8 3 | 9 10 11 12 4 | 5 | 13 14 15 16 6 | 17 18 19 20 7 | 21 22 23 24 8 | [[[1,2,3,4],[5,6,7,8],[9,10,11,12]],[[13,14,15,16],[17,18,19,20],[21,22,23,24]]] 9 | [1,2,3] 10 | [1,2,3;4,5,6] 11 | "abc" "def" 12 | "gh\ni" "abc" 13 | "def" "gh\ni" 14 | [["abc","def"],["gh\ni","abc"],["def","gh\ni"]] 15 | ["abc","def";"gh\ni","abc";"def","gh\ni"] 16 | 1.2 3.4 17 | 5.6J¯7.8 1.2 18 | 3.4 5.6J¯7.8 19 | 1.2,3.4 20 | 5.6J¯7.8,1.2 21 | 3.4,5.6J¯7.8 22 | 23 | [[1.2,3.4],[[5.6,-7.8],1.2],[3.4,[5.6,-7.8]]] 24 | [1.2,3.4;(5.6-7.8i),1.2;3.4,(5.6-7.8i)] 25 | A: 1 26 | b: zwei 27 | zeta: 1 2 3 28 | "A": 1 29 | "b": "zwei" 30 | "zeta": 1 2 3 31 | {"A":1,"b":"zwei","zeta":[1,2,3]} 32 | struct("A",1,"b","zwei","zeta",[1,2,3]) 33 | (1 2;(3;(1;2;);four;);5;) 34 | (1 2;(3;(1;2;);"four";);5;) 35 | [[1,2],[3,[1,2],"four"],5] 36 | 123 83 4660 37 | 123 83 291 38 | 0173 0123 0443 39 | 0x7B 0x53 0x123 40 | 0x7B 0x53 0x123 41 | 5539427541665710p-52 42 | -------------------------------------------------------------------------------- /cmd/apl/testdata/obj.apl: -------------------------------------------------------------------------------- 1 | ⍝ Create a dictionary and index-assign (change) a field 2 | B←`a`b`c#(2 3⍴⍳6;"alpha";3;) 3 | B[`c]←5 4 | B[`c]=5 ⍝ out: 1 5 | 6 | ⍝ A is a nested dictionary 7 | A←`x`y`z#(1;B;"ZZZ";) 8 | 9 | ⍝ Depth-assignment 10 | A[`y;`c]←"@" 11 | A[`y;`c] ⍝ out: @ 12 | A[`y;`a;2;2]←8 13 | A[`y;`a;2;] ⍝ out: 4 8 6 14 | A[`y;`a;2;2] ⍝ out: 8 15 | A[`y;`c]←1 2 3 16 | A[`y;`c] ⍝ out: 1 2 3 17 | -------------------------------------------------------------------------------- /cmd/apl/testdata/obj.out: -------------------------------------------------------------------------------- 1 | 1 2 | @ 3 | 4 8 6 4 | 8 5 | 1 2 3 6 | -------------------------------------------------------------------------------- /cmd/apl/testdata/parse.apl: -------------------------------------------------------------------------------- 1 | test←{X←`A ⍎⍕⍵ ⋄ X≡⍵} 2 | ⎕PP←0 3 | test 2 3 4⍴⍳24 4 | ⎕PP←¯1 5 | test 2 3 4⍴⍳24 6 | ⎕PP←¯2 7 | test 2 3 4⍴⍳24 8 | ⎕PP←¯3 9 | test 3 4⍴⍳24 -------------------------------------------------------------------------------- /cmd/apl/testdata/parse.out: -------------------------------------------------------------------------------- 1 | 1 2 | 1b 3 | 1b 4 | 1b 5 | -------------------------------------------------------------------------------- /cmd/apl/testdata/table.apl: -------------------------------------------------------------------------------- 1 | ⍝ Create a table 2 | D ← 2019.02.07T13.25 + ⍳3 3 | N ← `Peter`Jack`Thomas 4 | B ← 1b 0b 1b 5 | I ← 100 × ⍳3 6 | F ← 100÷⍳3 7 | C ← 1+0J1×⍳3 8 | M ← ((1.2 2.1 3.0;);(;);(7.8912345678;);) ⍝ Multiple values; not allowed in K 9 | 10 | ⍝ A table is created by transposing a dictionary. 11 | ⍝ A dictionary is created with the # primitive 12 | D ← `Time`Name`Mark`Count`Number`Comp`Mult#(D;N;B;I;F;C;M;) 13 | T← ⍉D 14 | 15 | ⍝ Print the table in standard form, which is equal to ⍕T 16 | "Default table format:" 17 | T 18 | 19 | "PP set to 2:" 20 | ⎕PP←2 21 | T 22 | ⎕PP←⍳0 ⍝ reset PP 23 | 24 | ⍝ Print the table in parsable form 25 | "Parsable table format:" 26 | ¯1⍕T 27 | 28 | ⍝ Print table in csv format 29 | "csv format:" 30 | `csv ⍕T 31 | 32 | ⍝ Custom formatting is created in a dictionary with the corresponding field names 33 | "custom format:" 34 | F←`Time#`alpha 35 | F[`Time]←`2006-01-02T15:04 ⍝ To format dates, use the desired result for the prototype date Jan 2, 2006-01-02 15:04:05 36 | F[`Count]←`0x%x ⍝ Hexadecimal 37 | F[`Comp]←`%.3f@%.1f ⍝ Amplitude @ angle in degree 38 | F 39 | F⍕T 40 | 41 | ⍝ Custom format in csv output 42 | "custom format with csv:" 43 | F[`CSV]←1 ⍝ Add the special key CSV with value 1 44 | F⍕T 45 | 46 | ⍝ Table aggregations 47 | T←⍉`A`B`C`D#(1.1 1.2 1.3;2.1 2.2 2.3; 3.1 3.2 3.3;1 2 1;) 48 | T 49 | "column sum:" 50 | T[;;+/] 51 | T[;`A`C;+/] 52 | 53 | "named aggregations:" 54 | T[;`A;`min`max #(⌊/;⌈/;)] 55 | 56 | "non-aggregating functions:" 57 | T[;`A;`min`max #(⌊\;⌈\;)] 58 | 59 | "individual functions per column:" 60 | T[;`A`C;`minA`maxC #(⌊/;⌈/;)] 61 | 62 | "group by D:" 63 | T[;`A`C`D;+/;`D] 64 | 65 | "grouping function:" 66 | T[;`D`A`C;+/;"D is even"#⊃({0=2|D};)] 67 | -------------------------------------------------------------------------------- /cmd/apl/testdata/table.out: -------------------------------------------------------------------------------- 1 | Default table format: 2 | Time Name Mark Count Number Comp Mult 3 | 2019.02.07T13.25.01.000 Peter 1 100 100 1J1 1.2 2.1 3 4 | 2019.02.07T13.25.02.000 Jack 0 200 50 1J2 5 | 2019.02.07T13.25.03.000 Thomas 1 300 33.3333 1J3 7.89123 6 | 7 | PP set to 2: 8 | Time Name Mark Count Number Comp Mult 9 | 2019.02.07T13.25.01.000 Peter 1 100 1E+02 1J1 1.2 2.1 3 10 | 2019.02.07T13.25.02.000 Jack 0 200 50 1J2 11 | 2019.02.07T13.25.03.000 Thomas 1 300 33 1J3 7.9 12 | 13 | Parsable table format: 14 | "Time" "Name" "Mark" "Count" "Number" "Comp" "Mult" 15 | 2019.02.07T13.25.01.000 "Peter" 1b 100 100 1J1 [1.2 2.1 3] 16 | 2019.02.07T13.25.02.000 "Jack" 0b 200 50 1J2 [] 17 | 2019.02.07T13.25.03.000 "Thomas" 1b 300 33.333333333333336 1J3 [7.8912345678] 18 | 19 | csv format: 20 | Time,Name,Mark,Count,Number,Comp,Mult 21 | 2019.02.07T13.25.01.000,Peter,1,100,100,1J1,1.2 2.1 3 22 | 2019.02.07T13.25.02.000,Jack,0,200,50,1J2, 23 | 2019.02.07T13.25.03.000,Thomas,1,300,33.3333,1J3,7.89123 24 | 25 | custom format: 26 | Time: 2006-01-02T15:04 27 | Count: 0x%x 28 | Comp: %.3f@%.1f 29 | Time Name Mark Count Number Comp Mult 30 | 2019-02-07T13:25 Peter 1 0x64 100 1.414@45.0 1.2 2.1 3 31 | 2019-02-07T13:25 Jack 0 0xc8 50 2.236@63.4 32 | 2019-02-07T13:25 Thomas 1 0x12c 33.3333 3.162@71.6 7.89123 33 | 34 | custom format with csv: 35 | Time,Name,Mark,Count,Number,Comp,Mult 36 | 2019-02-07T13:25,Peter,1,0x64,100,1.414@45.0,1.2 2.1 3 37 | 2019-02-07T13:25,Jack,0,0xc8,50,2.236@63.4, 38 | 2019-02-07T13:25,Thomas,1,0x12c,33.3333,3.162@71.6,7.89123 39 | 40 | A B C D 41 | 1.1 2.1 3.1 1 42 | 1.2 2.2 3.2 2 43 | 1.3 2.3 3.3 1 44 | 45 | column sum: 46 | A B C D 47 | 3.6 6.6 9.6 4 48 | 49 | A C 50 | 3.6 9.6 51 | 52 | named aggregations: 53 | min max 54 | 1.1 1.3 55 | 56 | non-aggregating functions: 57 | min max 58 | 1.1 1.1 59 | 1.1 1.2 60 | 1.1 1.3 61 | 62 | individual functions per column: 63 | minA maxC 64 | 1.1 3.3 65 | 66 | group by D: 67 | D A C 68 | 1 2.4 6.4 69 | 2 1.2 3.2 70 | 71 | grouping function: 72 | D is even A C 73 | 0 2.4 6.4 74 | 1 1.2 3.2 75 | 76 | -------------------------------------------------------------------------------- /cmd/iv.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/ktye/iv/apl" 7 | ) 8 | 9 | func Iv(a *apl.Apl, p string, w io.Writer) error { 10 | a.SetOutput(w) 11 | if err := a.ParseAndEval(`r←{<⍤⍵ io→r 0}⋄s←{⍵⍴<⍤0 io→r 0}`); err != nil { 12 | return err 13 | } 14 | return a.ParseAndEval(p) 15 | } 16 | -------------------------------------------------------------------------------- /cmd/iv/README.md: -------------------------------------------------------------------------------- 1 | # iv - APL stream processor 2 | 3 | Program *iv* is similar to `cmd/apl` but reads data from stdin instead of program text. 4 | 5 | The program is given as command line arguments. 6 | 7 | ``` 8 | Usage 9 | cat data | iv COMMANDS 10 | ``` 11 | 12 | ## streaming data 13 | Iv provides two pre-defined functions that read data from stdin: 14 | ``` 15 | r ← {<⍤⍵ io→r 0} 16 | s ← {⍵⍴<⍤0 io→r 0} 17 | ``` 18 | io→r 0 returns a channel that provides a line of input on each read. 19 | 20 | The rank operator `⍤` is extended to parse sub-arrays from a channel delivering strings. 21 | See `ScanRankArray` in `apl/fmt.go`. 22 | The number and array syntax is not as strict as usual program text to allow interoperatiliby with other programs. 23 | 24 | Function `r` is called monadically with a rank argument: `r 0` reads a scalar at a time, `r 1` a vector at a time and so on. 25 | Usually a vector is a line and higher order arrays are terminated by multiple newlines. 26 | Bracket notation (json arrays) or matlab style `1 2 3;4 5 6` can also be used. 27 | 28 | Function `s` ignores the structure of incoming data and always reads a scalar at a time, reshaping it according to it's right argument. 29 | 30 | ## examples 31 | To apply a function on each 2d subarray of the input stream, we can call iv with: 32 | ``` 33 | cat data | iv f¨r 2 34 | ``` 35 | 36 | 37 | Format each 3 2 subarray of the input stream to a json string: 38 | ``` 39 | cat data | iv '`json ⍕¨s 2 3' 40 | ``` 41 | 42 | More examples are given in `testdata`. 43 | 44 | ## extra libraries 45 | cmd/iv includes only the base packages. Even `io` mentioned above only provides a single function to read from stdin. 46 | To use all packages in this repository, cmd/lui can be called to work like iv by including the argument `-i`. 47 | ``` 48 | lui -i COMMANDS 49 | ``` 50 | -------------------------------------------------------------------------------- /cmd/iv/iv_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/ktye/iv/cmd" 8 | ) 9 | 10 | func init() { 11 | if err := os.Chdir("testdata"); err != nil { 12 | panic(err) 13 | } 14 | } 15 | 16 | func TestIv(t *testing.T) { 17 | if err := cmd.IvTest(newApl); err != nil { 18 | t.Fatal(err) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cmd/iv/main.go: -------------------------------------------------------------------------------- 1 | // APL stream processor. 2 | // 3 | // Usage 4 | // cat data | iv COMMANDS 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "os" 11 | "strings" 12 | 13 | "github.com/ktye/iv/apl" 14 | "github.com/ktye/iv/apl/numbers" 15 | "github.com/ktye/iv/apl/operators" 16 | "github.com/ktye/iv/apl/primitives" 17 | "github.com/ktye/iv/cmd" 18 | ) 19 | 20 | var stdin io.ReadCloser = os.Stdin 21 | 22 | func main() { 23 | if len(os.Args) < 2 { 24 | fatal(fmt.Errorf("arguments expected")) 25 | } 26 | a := newApl(stdin) 27 | fatal(cmd.Iv(a, strings.Join(os.Args[1:], " "), os.Stdout)) 28 | } 29 | 30 | func newApl(r io.ReadCloser) *apl.Apl { 31 | stdin = r 32 | a := apl.New(nil) 33 | numbers.Register(a) 34 | primitives.Register(a) 35 | operators.Register(a) 36 | 37 | // Add a minimal io package that's sole purpose is to allow 38 | // to read from stdin. 39 | pkg := map[string]apl.Value{ 40 | "r": apl.ToFunction(readfd), 41 | } 42 | a.RegisterPackage("io", pkg) 43 | return a 44 | } 45 | 46 | func fatal(err error) { 47 | if err != nil { 48 | fmt.Fprintln(os.Stderr, err) 49 | os.Exit(1) 50 | } 51 | } 52 | 53 | func readfd(a *apl.Apl, _, R apl.Value) (apl.Value, error) { 54 | fd, ok := R.(apl.Int) 55 | if ok == false { 56 | return nil, fmt.Errorf("iv/io read: argument must be 0 (stdin)") 57 | } 58 | if fd != 0 { 59 | return nil, fmt.Errorf("monadic <: right argument must be 0 (stdin)") 60 | } 61 | return apl.LineReader(stdin), nil 62 | } 63 | -------------------------------------------------------------------------------- /cmd/iv/testdata/a.iv: -------------------------------------------------------------------------------- 1 | # 1 + 1 2 | -------------------------------------------------------------------------------- /cmd/iv/testdata/a.out: -------------------------------------------------------------------------------- 1 | 2 2 | -------------------------------------------------------------------------------- /cmd/iv/testdata/colsum.iv: -------------------------------------------------------------------------------- 1 | # {⍵,⍪+/⍵}¨r 2 2 | 1 2 3 3 | 4 5 6 4 | 5 | 7 8 9 6 | 1 2 3 -------------------------------------------------------------------------------- /cmd/iv/testdata/colsum.out: -------------------------------------------------------------------------------- 1 | 1 2 3 6 2 | 4 5 6 15 3 | 7 8 9 24 4 | 1 2 3 6 5 | -------------------------------------------------------------------------------- /cmd/iv/testdata/rank.iv: -------------------------------------------------------------------------------- 1 | # {(⍵;⍴⍵;)}¨r 2 2 | 1 2 3 3 | 4 5 6 4 | 5 | 7 8 9 6 | 1 2 3 7 | -------------------------------------------------------------------------------- /cmd/iv/testdata/rank.out: -------------------------------------------------------------------------------- 1 | ( 1 2 3 2 | 4 5 6;2 3;) 3 | ( 7 8 9 4 | 1 2 3;2 3;) 5 | -------------------------------------------------------------------------------- /cmd/iv/testdata/shape.iv: -------------------------------------------------------------------------------- 1 | # `json ⍕¨s 3 2 2 | 1 2 3 4 5 3 | 6 7 8 9 0 4 | 1 2 -------------------------------------------------------------------------------- /cmd/iv/testdata/shape.out: -------------------------------------------------------------------------------- 1 | [[1,2],[3,4],[5,6]] 2 | [[7,8],[9,0],[1,2]] 3 | -------------------------------------------------------------------------------- /cmd/testing.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "os" 10 | "strings" 11 | 12 | "github.com/ktye/iv/apl" 13 | ) 14 | 15 | // AplTest is run from go test in cmd/apl and cmd/lui. 16 | func AplTest(newapl func() *apl.Apl) error { 17 | d, err := os.Open(".") 18 | if err != nil { 19 | return err 20 | } 21 | defer d.Close() 22 | 23 | files, err := d.Readdirnames(-1) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | for _, file := range files { 29 | if strings.HasSuffix(file, ".apl") == false { 30 | continue 31 | } 32 | if err := testAplFile(newapl, file); err != nil { 33 | return err 34 | } 35 | } 36 | return nil 37 | } 38 | 39 | // IvTest is run from go test in cmd/iv and cmd/lui. 40 | func IvTest(newapl func(io.ReadCloser) *apl.Apl) error { 41 | d, err := os.Open(".") 42 | if err != nil { 43 | return err 44 | } 45 | defer d.Close() 46 | 47 | files, err := d.Readdirnames(-1) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | for _, file := range files { 53 | if strings.HasSuffix(file, ".iv") == false { 54 | continue 55 | } 56 | if err := testIvFile(newapl, file[:len(file)-3]); err != nil { 57 | return fmt.Errorf("%s: %s", file, err) 58 | } 59 | } 60 | return nil 61 | } 62 | 63 | func testAplFile(newapl func() *apl.Apl, file string) error { 64 | var out bytes.Buffer 65 | a := newapl() 66 | a.SetOutput(&out) 67 | if err := Apl(a, nil, []string{file}); err != nil { 68 | return compareError(err, file) 69 | } 70 | return compareOut(out.Bytes(), file[:len(file)-4]) 71 | } 72 | func testIvFile(newapl func(io.ReadCloser) *apl.Apl, file string) error { 73 | f, err := os.Open(file + ".iv") 74 | if err != nil { 75 | return err 76 | } 77 | defer f.Close() 78 | 79 | // First line in each iv test file is the program with a comment. 80 | r := bufio.NewReader(f) 81 | prog, err := readline(r) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | var out bytes.Buffer 87 | if err := Iv(newapl(ioutil.NopCloser(r)), prog[1:], &out); err != nil { 88 | return err 89 | } 90 | return compareOut(out.Bytes(), file) 91 | } 92 | 93 | func readline(s io.RuneScanner) (string, error) { 94 | var b strings.Builder 95 | for { 96 | if r, _, err := s.ReadRune(); err != nil { 97 | return "", err 98 | } else { 99 | if r == '\n' { 100 | return b.String(), nil 101 | } 102 | b.WriteRune(r) 103 | } 104 | } 105 | } 106 | 107 | func compareError(got error, file string) error { 108 | name := file[:len(file)-4] + ".err" 109 | want, err := ioutil.ReadFile(name) 110 | if err != nil { 111 | return fmt.Errorf("%s: failed but (%s)\n%s", file, err, got) 112 | } 113 | if got := got.Error() + "\n"; got != string(want) { 114 | return fmt.Errorf("%s: expected:\n%sgot:\n%s", file, want, got) 115 | } 116 | return nil 117 | } 118 | 119 | func compareOut(got []byte, file string) error { 120 | name := file + ".out" 121 | want, err := ioutil.ReadFile(name) 122 | if err != nil { 123 | return fmt.Errorf("%s: should have failed, but: %s", file, err) 124 | } 125 | 126 | at := -1 127 | max := len(want) 128 | if len(got) < max { 129 | max = len(got) 130 | } 131 | if len(want) != len(got) { 132 | at = max 133 | } 134 | 135 | line := 1 136 | for i := 0; i < max; i++ { 137 | if want[i] == '\n' { 138 | line++ 139 | } 140 | if got[i] != want[i] { 141 | at = i 142 | break 143 | } 144 | } 145 | 146 | if at < 0 { 147 | return nil 148 | } 149 | return fmt.Errorf("%s:%d differs (byte %d). Got:\n%s\nWant:\n%s", name, line, at+1, string(got), string(want)) 150 | } 151 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ktye/iv 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /log.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | --------------------------------------------------------------------------------