aboutsummaryrefslogtreecommitdiff
path: root/neb/std
diff options
context:
space:
mode:
Diffstat (limited to 'neb/std')
-rw-r--r--neb/std/fs.py80
-rw-r--r--neb/std/types.py7
2 files changed, 82 insertions, 5 deletions
diff --git a/neb/std/fs.py b/neb/std/fs.py
index 495c7ea..d20cde9 100644
--- a/neb/std/fs.py
+++ b/neb/std/fs.py
@@ -2,9 +2,15 @@ from .. import TypeEnum, Environment, Arg, Builtin, evaluate, InterpretPanic
from ..structs import *
from pathlib import Path
from glob import glob
+from io import UnsupportedOperation
+import sys
FS = Environment()
+FS.register("_stdin_", Handle(sys.stdin))
+FS.register("_stdout_", Handle(sys.stdout))
+FS.register("_stderr_", Handle(sys.stderr))
+
def interpretExists(symbol, args, env, ns):
return Bool(Path(args[0].value).resolve().exists())
@@ -39,13 +45,77 @@ def interpretWithWrite(symbol, args, env, ns):
FS.register("with-write", Builtin("with-write", interpretWithWrite, [Arg("filename", TypeEnum.STRING)], Arg("exprs", TypeEnum.ANY, lazy=True)))
def interpretWrite(symbol, args, env, ns):
- # write :string :filehandle
- line = args[0]
+ string = args[0]
handle = args[1]
- handle.args[0].write(line.value) # TODO wrong! how do we evaluate a handle?
- return Literal([])
+ try:
+ handle.file.write(string.value)
+ except UnsupportedOperation:
+ raise InterpretPanic(symbol, f"{handle} is not writable!")
+ except ValueError:
+ raise InterpretPanic(symbol, f"{handle} is closed")
+ return List([])
+
+FS.register("write", Builtin("write", interpretWrite, [Arg("string", TypeEnum.STRING), Arg("handle", TypeEnum.HANDLE)], return_type=TypeEnum.LIST))
+
+def interpretOpenRead(symbol, args, env, ns):
+ name = args[0].value
+ fil = Path(name)
+ if not fil.exists():
+ raise InterpretPanic(symbol, "file does not exist", fil)
+ try:
+ f = open(str(fil), "r")
+ except:
+ raise InterpretPanic(symbol, "cannot open {fil} for reading")
+ return Handle(f)
+
+FS.register("open-read", Builtin("open-read", interpretOpenRead, [Arg("filename", TypeEnum.STRING)], return_type=TypeEnum.HANDLE))
+
+def interpretOpenWrite(symbol, args, env, ns):
+ name = args[0].value
+ fil = Path(name)
+ try:
+ f = open(str(fil), "w")
+ except:
+ raise InterpretPanic(symbol, "cannot open {fil} for writing")
+ return Handle(f)
+
+FS.register("open-write", Builtin("open-write", interpretOpenWrite, [Arg("filename", TypeEnum.STRING)], return_type=TypeEnum.HANDLE))
+
+def interpretOpenAppend(symbol, args, env, ns):
+ name = args[0].value
+ fil = Path(name)
+ try:
+ f = open(str(fil), "a")
+ except:
+ raise InterpretPanic(symbol, "cannot open {fil} for appending")
+ return Handle(f)
+
+FS.register("open-append", Builtin("open-append", interpretOpenAppend, [Arg("filename", TypeEnum.STRING)], return_type=TypeEnum.HANDLE))
+
+def interpretClose(symbol, args, env, ns):
+ try:
+ args[0].file.close()
+ # TODO ideally we'd be able to remove it from the env
+ # but by this point, we don't know its symbol
+ # though it may not be in the env, e.g.
+ # (close (print (read (open-read "fil.txt"))))
+ except:
+ raise InterpretPanic(symbol, "cannot close {args[0]}")
+ return List([])
+
+FS.register("close", Builtin("close", interpretClose, [Arg("handle", TypeEnum.HANDLE)], return_type=TypeEnum.LIST))
+
+def interpretRead(symbol, args, env, ns):
+ handle = args[0]
+ try:
+ inp = args[0].file.read()
+ except UnsupportedOperation:
+ raise InterpretPanic(symbol, f"{handle} is not writable!")
+ except ValueError:
+ raise InterpretPanic(symbol, f"{handle} is closed")
+ return String(inp)
-FS.register("write", Builtin("write", interpretWrite, [Arg("string", TypeEnum.STRING), Arg("filename", TypeEnum.LIST)]))
+FS.register("read", Builtin("read", interpretRead, [Arg("handle", TypeEnum.HANDLE)], return_type=TypeEnum.STRING))
def interpretReadLines(symbol, args, env, ns):
target_file_name = args[0].value
diff --git a/neb/std/types.py b/neb/std/types.py
index f8d5405..872f29a 100644
--- a/neb/std/types.py
+++ b/neb/std/types.py
@@ -69,6 +69,11 @@ def interpretIsLiteral(symbol, args, env, ns):
TYPES.register("literal?", Builtin("literal?", interpretIsLiteral, [Arg("arg", TypeEnum.ANY)], return_type=Type(":bool")))
+def interpretIsHandle(symbol, args, env, ns):
+ return Bool(isinstance(args[0], Handle))
+
+TYPES.register("handle?", Builtin("handle?", interpretIsHandle, [Arg("arg", TypeEnum.ANY)], return_type=Type(":bool")))
+
# add types to env
any_type = NebType(":any", None, interpretIsAny)
literal_type = NebType(":literal", any_type, interpretIsLiteral)
@@ -78,6 +83,7 @@ bool_type = NebType(":bool", literal_type, interpretIsBool)
number_type = NebType(":number", literal_type, interpretIsNumber)
int_type = NebType(":int", number_type, interpretIsInt)
float_type = NebType(":float", number_type, interpretIsFloat)
+handle_type = NebType(":handle", any_type, interpretIsHandle)
TYPES.register(":any", any_type)
TYPES.register(":literal", literal_type)
@@ -87,3 +93,4 @@ TYPES.register(":bool", bool_type)
TYPES.register(":number", number_type)
TYPES.register(":int", int_type)
TYPES.register(":float", float_type)
+TYPES.register(":handle", handle_type)