From 67e88de3db3e02aa2807231f2ae4ec95c55beed3 Mon Sep 17 00:00:00 2001 From: syuparn Date: Sat, 30 Dec 2023 12:29:59 +0900 Subject: [PATCH] props: add Kernel#read --- evaluator/eval_test.go | 30 ++++++++++++++++++++++++++++++ evaluator/testdata/sample.txt | 1 + props/kernel_props.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 evaluator/testdata/sample.txt diff --git a/evaluator/eval_test.go b/evaluator/eval_test.go index 37ef0bb..4c74da1 100644 --- a/evaluator/eval_test.go +++ b/evaluator/eval_test.go @@ -10379,6 +10379,36 @@ func TestEvalAssertRaises(t *testing.T) { } } +func TestEvalRead(t *testing.T) { + tests := []struct { + input string + expected object.PanObject + }{ + { + `read("testdata/sample.txt")`, + object.NewPanStr("dummy\n"), + }, + { + `read()`, + object.NewTypeErr("read requires at least 1 arg"), + }, + { + `read(1)`, + object.NewTypeErr("1 cannot be treated as str"), + }, + { + `read("testdata/notfound.txt")`, + object.NewFileNotFoundErr(`"testdata/notfound.txt" cannot be opened: open testdata/notfound.txt: no such file or directory`), + }, + // TODO: add non-text options + } + + for _, tt := range tests { + actual := testEval(t, tt.input) + testValue(t, actual, tt.expected) + } +} + func TestEvalTry(t *testing.T) { tests := []struct { input string diff --git a/evaluator/testdata/sample.txt b/evaluator/testdata/sample.txt new file mode 100644 index 0000000..421376d --- /dev/null +++ b/evaluator/testdata/sample.txt @@ -0,0 +1 @@ +dummy diff --git a/props/kernel_props.go b/props/kernel_props.go index bedaca1..98b7959 100644 --- a/props/kernel_props.go +++ b/props/kernel_props.go @@ -3,6 +3,7 @@ package props import ( "fmt" "os" + "path/filepath" "github.com/Syuparn/pangaea/object" ) @@ -123,5 +124,37 @@ func KernelProps(propContainer map[string]object.PanObject) map[string]object.Pa ), "import": propContainer["Kernel_import"], "invite!": propContainer["Kernel_invite!"], + "read": f( + func( + env *object.Env, kwargs *object.PanObj, args ...object.PanObject, + ) object.PanObject { + if len(args) < 1 { + return object.NewTypeErr("read requires at least 1 arg") + } + + pathObj, ok := object.TraceProtoOfStr(args[0]) + if !ok { + return object.NewTypeErr( + fmt.Sprintf("%s cannot be treated as str", args[0].Repr())) + } + + filePath := pathObj.Value + // HACK: find relative file path + if p, ok := env.Get(object.GetSymHash(object.SourcePathVar)); ok { + if p.Type() != object.StrType { + return object.NewTypeErr(fmt.Sprintf("%s %s must be str", object.SourcePathVar, p.Inspect())) + } + filePath = filepath.Join(filepath.Dir(filePath), filePath) + } + + b, err := os.ReadFile(filePath) + if err != nil { + return object.NewFileNotFoundErr( + fmt.Sprintf("%s cannot be opened: %s", args[0].Repr(), err.Error())) + } + + return object.NewPanStr(string(b)) + }, + ), } }