2024-04-10 10:45:58 +00:00
|
|
|
package cmdlang
|
|
|
|
|
|
|
|
import (
|
2024-04-10 11:58:06 +00:00
|
|
|
"bufio"
|
2024-04-10 10:45:58 +00:00
|
|
|
"context"
|
2024-04-13 11:46:50 +00:00
|
|
|
"errors"
|
2024-04-10 12:19:11 +00:00
|
|
|
"fmt"
|
2024-04-10 11:58:06 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
2024-04-10 10:45:58 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2024-04-10 11:58:06 +00:00
|
|
|
func echoBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
2024-04-10 10:45:58 +00:00
|
|
|
if len(args.args) == 0 {
|
2024-04-12 23:25:16 +00:00
|
|
|
if _, err := fmt.Fprintln(args.inst.Out()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-04-13 11:46:50 +00:00
|
|
|
return nil, nil
|
2024-04-10 10:45:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var line strings.Builder
|
|
|
|
for _, arg := range args.args {
|
2024-04-10 12:19:11 +00:00
|
|
|
if s, ok := arg.(fmt.Stringer); ok {
|
|
|
|
line.WriteString(s.String())
|
|
|
|
}
|
2024-04-10 10:45:58 +00:00
|
|
|
}
|
2024-04-10 11:58:06 +00:00
|
|
|
|
2024-04-12 23:25:16 +00:00
|
|
|
if _, err := fmt.Fprintln(args.inst.Out(), line.String()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return nil, nil
|
2024-04-10 11:58:06 +00:00
|
|
|
}
|
|
|
|
|
2024-04-11 10:58:59 +00:00
|
|
|
func setBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|
|
|
if err := args.expectArgn(2); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
name, err := args.stringArg(0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
newVal := args.args[1]
|
|
|
|
|
|
|
|
// TODO: if the value is a stream, consume the stream and save it as a list
|
|
|
|
args.ec.setVar(name, newVal)
|
|
|
|
return newVal, nil
|
|
|
|
}
|
|
|
|
|
2024-04-11 10:47:59 +00:00
|
|
|
func toUpperBuiltin(ctx context.Context, inStream stream, args invocationArgs) (object, error) {
|
2024-04-10 11:58:06 +00:00
|
|
|
// Handle args
|
|
|
|
return mapFilterStream{
|
2024-04-11 10:47:59 +00:00
|
|
|
in: inStream,
|
2024-04-10 11:58:06 +00:00
|
|
|
mapFn: func(x object) (object, bool) {
|
2024-04-11 12:05:05 +00:00
|
|
|
s, ok := x.(strObject)
|
2024-04-10 11:58:06 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
2024-04-11 12:05:05 +00:00
|
|
|
return strObject(strings.ToUpper(string(s))), true
|
2024-04-10 11:58:06 +00:00
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func catBuiltin(ctx context.Context, args invocationArgs) (object, error) {
|
|
|
|
if err := args.expectArgn(1); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-04-10 12:19:11 +00:00
|
|
|
filename, err := args.stringArg(0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &fileLinesStream{filename: filename}, nil
|
2024-04-10 11:58:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type fileLinesStream struct {
|
|
|
|
filename string
|
|
|
|
f *os.File
|
|
|
|
scnr *bufio.Scanner
|
|
|
|
}
|
|
|
|
|
2024-04-11 12:05:05 +00:00
|
|
|
func (f *fileLinesStream) String() string {
|
|
|
|
return fmt.Sprintf("fileLinesStream{file: %v}", f.filename)
|
|
|
|
}
|
|
|
|
|
2024-04-13 11:46:50 +00:00
|
|
|
func (f *fileLinesStream) Truthy() bool {
|
|
|
|
return true // ??
|
|
|
|
}
|
|
|
|
|
2024-04-10 11:58:06 +00:00
|
|
|
func (f *fileLinesStream) next() (object, error) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
// We open the file on the first pull. That way, an unconsumed stream won't result in a FD leak
|
|
|
|
if f.f == nil {
|
|
|
|
f.f, err = os.Open(f.filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
f.scnr = bufio.NewScanner(f.f)
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.scnr.Scan() {
|
2024-04-11 12:05:05 +00:00
|
|
|
return strObject(f.scnr.Text()), nil
|
2024-04-10 11:58:06 +00:00
|
|
|
}
|
|
|
|
if f.scnr.Err() == nil {
|
|
|
|
return nil, io.EOF
|
|
|
|
}
|
|
|
|
return nil, f.scnr.Err()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fileLinesStream) close() error {
|
|
|
|
if f.f != nil {
|
|
|
|
return f.f.Close()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-04-13 11:46:50 +00:00
|
|
|
func ifBuiltin(ctx context.Context, args macroArgs) (object, error) {
|
|
|
|
if args.nargs() < 2 {
|
|
|
|
return nil, errors.New("need at least 2 arguments")
|
|
|
|
}
|
2024-04-10 11:58:06 +00:00
|
|
|
|
2024-04-13 11:46:50 +00:00
|
|
|
if guard, err := args.evalArg(ctx, 0); err == nil && isTruthy(guard) {
|
|
|
|
return args.evalBlock(ctx, 1)
|
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
args.shift(2)
|
|
|
|
for args.identIs(ctx, 0, "elif") {
|
|
|
|
args.shift(1)
|
2024-04-10 11:58:06 +00:00
|
|
|
|
2024-04-13 11:46:50 +00:00
|
|
|
if args.nargs() < 2 {
|
|
|
|
return nil, errors.New("need at least 2 arguments")
|
|
|
|
}
|
|
|
|
|
|
|
|
if guard, err := args.evalArg(ctx, 0); err == nil && isTruthy(guard) {
|
|
|
|
return args.evalBlock(ctx, 1)
|
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
args.shift(2)
|
|
|
|
}
|
|
|
|
|
|
|
|
if args.identIs(ctx, 0, "else") && args.nargs() > 1 {
|
|
|
|
return args.evalBlock(ctx, 1)
|
|
|
|
} else if args.nargs() == 0 {
|
|
|
|
// no elif or else
|
|
|
|
return nil, nil
|
2024-04-10 11:58:06 +00:00
|
|
|
}
|
|
|
|
|
2024-04-13 11:46:50 +00:00
|
|
|
return nil, errors.New("malformed if-elif-else")
|
2024-04-10 10:45:58 +00:00
|
|
|
}
|