You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
903 lines
18 KiB
903 lines
18 KiB
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.rawtext.club/sloum/nimf/termios"
|
|
)
|
|
|
|
var builtins = make(map[string]func())
|
|
|
|
func initBuiltins() {
|
|
// Math
|
|
builtins["+"] = add
|
|
builtins["-"] = subtract
|
|
builtins["*"] = multiply
|
|
builtins["/"] = divide
|
|
builtins["%"] = mod
|
|
builtins["&"] = bitAnd
|
|
builtins["|"] = bitOr
|
|
builtins["xor"] = bitXor
|
|
builtins["<<"] = bitLeftShift
|
|
builtins[">>"] = bitRightShift
|
|
|
|
// Stack
|
|
builtins["drop"] = drop
|
|
builtins["dup"] = dup
|
|
builtins["swap"] = swap
|
|
builtins["over"] = over
|
|
builtins["roll"] = roll
|
|
builtins["pick"] = pick
|
|
builtins[".s"] = showStack
|
|
builtins[".r"] = showAddressStack
|
|
builtins["<r"] = moveToAddressStack
|
|
builtins["r>"] = moveFromAddressStack
|
|
builtins["r@"] = copyFromAddressStack
|
|
builtins["rdepth"] = rdepth
|
|
builtins["depth"] = depth
|
|
builtins["clearstack"] = clearStack
|
|
|
|
// Logical
|
|
builtins[">"] = gt
|
|
builtins["<"] = lt
|
|
builtins["="] = equal
|
|
|
|
// System
|
|
builtins["bye"] = bye
|
|
builtins["halt"] = sysExit
|
|
builtins["words"] = words
|
|
builtins["sleep"] = sleep
|
|
builtins["see"] = showSubroutine
|
|
builtins["if"] = nimfIf
|
|
builtins["else"] = nimfElse
|
|
builtins["then"] = nimfThen
|
|
builtins["do"] = do
|
|
builtins["loop"] = loop
|
|
builtins["error"] = throwError
|
|
builtins["winsize"] = winsize
|
|
builtins["timenow"] = timenow
|
|
builtins["get-env"] = getEnv
|
|
builtins["exit"] = exitWord
|
|
|
|
// Memory
|
|
builtins["var"] = variable
|
|
builtins["svar"] = stringVariable
|
|
builtins["allot"] = allotMemory
|
|
builtins["!"] = writeToMemory
|
|
builtins["set"] = writeToMemory
|
|
builtins["s!"] = writeStringToMemory
|
|
builtins["set-string"] = writeStringToMemory
|
|
builtins["@"] = readFromMemory
|
|
builtins["get"] = readFromMemory
|
|
builtins["s@"] = readStringIntoTempBuffer
|
|
builtins["get-string"] = readStringIntoTempBuffer
|
|
builtins["sr@"] = readRawStringIntoTempBuffer
|
|
builtins["get-raw-string"] = readRawStringIntoTempBuffer
|
|
builtins["+!"] = memoryValueAddition
|
|
builtins["local"] = localVar
|
|
builtins["lallot"] = localAllotMemory
|
|
|
|
// IO
|
|
builtins[","] = output
|
|
builtins["cr"] = newline
|
|
builtins["space"] = space
|
|
builtins["input"] = bufferedInputRequest
|
|
builtins["key"] = getKeyPress
|
|
builtins["emit"] = emit
|
|
builtins["tcp.connect"] = connect
|
|
builtins["tcp.close"] = connClose
|
|
builtins["tcp.write"] = connWrite
|
|
builtins["tcp.read"] = connReadAll
|
|
builtins["file.open"] = openFile
|
|
builtins["file.exists?"] = fileExists
|
|
builtins["file.read"] = readLine
|
|
builtins["file.write"] = writeToFile
|
|
builtins["file.close"] = closeFile
|
|
builtins["subproc.create"] = addSubprocess
|
|
builtins["subproc.send"] = pipeToSubprocess
|
|
builtins["subproc.exec"] = runSubprocess
|
|
builtins["subproc.cd"] = procWorkingDir
|
|
builtins["subproc.ready?"] = nilSubprocess
|
|
|
|
builtins["pwd"] = getPwd
|
|
builtins["cd"] = changeDir
|
|
}
|
|
|
|
//==================
|
|
// MATH
|
|
//==================
|
|
|
|
func add() {
|
|
data.Push(data.Pop() + data.Pop())
|
|
}
|
|
|
|
func subtract() {
|
|
second := data.Pop()
|
|
data.Push(data.Pop() - second)
|
|
}
|
|
|
|
func multiply() {
|
|
data.Push(data.Pop() * data.Pop())
|
|
}
|
|
|
|
func divide() {
|
|
second := data.Pop()
|
|
data.Push(data.Pop() / second)
|
|
}
|
|
|
|
func mod() {
|
|
second := data.Pop()
|
|
data.Push(data.Pop() % second)
|
|
}
|
|
|
|
func bitAnd() {
|
|
second := data.Pop()
|
|
data.Push(data.Pop() & second)
|
|
}
|
|
|
|
func bitOr() {
|
|
second := data.Pop()
|
|
data.Push(data.Pop() | second)
|
|
}
|
|
|
|
func bitXor() {
|
|
second := data.Pop()
|
|
data.Push(data.Pop() ^ second)
|
|
}
|
|
|
|
func bitLeftShift() {
|
|
second := data.Pop()
|
|
data.Push(data.Pop() << uint(second))
|
|
}
|
|
|
|
func bitRightShift() {
|
|
second := data.Pop()
|
|
data.Push(data.Pop() >> uint(second))
|
|
}
|
|
|
|
//==================
|
|
// STACK
|
|
//==================
|
|
|
|
func showStack() {
|
|
fmt.Printf("<%d> %v ", data.Pointer+1, data.stack[:data.Pointer+1])
|
|
memory.Set(outputPrintedFlag, -1)
|
|
}
|
|
|
|
func showAddressStack() {
|
|
fmt.Printf("<%d> %v ", address.Pointer+1, address.stack[:address.Pointer+1])
|
|
memory.Set(outputPrintedFlag, -1)
|
|
}
|
|
|
|
func clearStack() {
|
|
data.Pointer = -1
|
|
}
|
|
|
|
func depth() {
|
|
data.Push(data.Pointer + 1)
|
|
}
|
|
|
|
func drop() {
|
|
data.Pop()
|
|
}
|
|
|
|
func roll() {
|
|
count := data.Peep()
|
|
if count <= 0 {
|
|
data.Pop()
|
|
return
|
|
}
|
|
swap()
|
|
address.Push(data.Pop())
|
|
data.Push(1)
|
|
subtract()
|
|
roll()
|
|
data.Push(address.Pop())
|
|
swap()
|
|
}
|
|
|
|
func pick() {
|
|
count := data.Pop()
|
|
if count <= 0 {
|
|
return
|
|
}
|
|
if data.Pointer-count < 0 {
|
|
handleError(fmt.Errorf("Stack underflow"))
|
|
return
|
|
}
|
|
|
|
data.Push(data.stack[data.Pointer-count])
|
|
}
|
|
|
|
func swap() {
|
|
first := data.Pop()
|
|
last := data.Pop()
|
|
data.Push(first)
|
|
data.Push(last)
|
|
}
|
|
|
|
func over() {
|
|
first := data.Pop()
|
|
last := data.Pop()
|
|
data.Push(last)
|
|
data.Push(first)
|
|
data.Push(last)
|
|
}
|
|
|
|
func dup() {
|
|
item := data.Pop()
|
|
data.Push(item)
|
|
data.Push(item)
|
|
}
|
|
|
|
func moveToAddressStack() {
|
|
address.Push(data.Pop())
|
|
}
|
|
|
|
func moveFromAddressStack() {
|
|
data.Push(address.Pop())
|
|
}
|
|
|
|
func copyFromAddressStack() {
|
|
data.Push(address.Peep())
|
|
}
|
|
|
|
func rdepth() {
|
|
data.Push(address.Pointer + 1)
|
|
}
|
|
|
|
//==================
|
|
// LOGICAL
|
|
//==================
|
|
|
|
func gt() {
|
|
data.Push(bool2Int((data.Pop() < data.Pop())))
|
|
}
|
|
|
|
func lt() {
|
|
data.Push(bool2Int((data.Pop() > data.Pop())))
|
|
}
|
|
|
|
func equal() {
|
|
data.Push(bool2Int((data.Pop() == data.Pop())))
|
|
}
|
|
|
|
//==================
|
|
// SYSTEM
|
|
//==================
|
|
|
|
func bye() {
|
|
os.Exit(0)
|
|
}
|
|
|
|
func timenow() {
|
|
now := time.Now().Unix()
|
|
data.Push(int(now))
|
|
}
|
|
|
|
func sysExit() {
|
|
code := data.Pop()
|
|
os.Exit(code)
|
|
}
|
|
|
|
func throwError() {
|
|
if memory.Get(actionModeFlag) == 0 {
|
|
handleError(fmt.Errorf("The word %q was found outside of a subroutine body", "error"))
|
|
return
|
|
}
|
|
length := memory.Get(tempString)
|
|
var out strings.Builder
|
|
if length < 1 {
|
|
out.WriteString("Generic error, no message provided")
|
|
} else {
|
|
intSlice := memory.memory[51 : 50+memory.memory[50]+1]
|
|
for _, num := range intSlice {
|
|
out.WriteRune(rune(num))
|
|
}
|
|
}
|
|
handleError(fmt.Errorf("%s", out.String()))
|
|
}
|
|
|
|
func sleep() {
|
|
amount := data.Pop()
|
|
time.Sleep(time.Duration(amount) * time.Millisecond)
|
|
}
|
|
|
|
func words() {
|
|
wordList := make([]string, 0, 25)
|
|
for k := range builtins {
|
|
wordList = append(wordList, k)
|
|
}
|
|
for k := range names {
|
|
if strings.Index(k, ".private.") > -1 {
|
|
continue
|
|
}
|
|
wordList = append(wordList, k)
|
|
}
|
|
sort.Strings(wordList)
|
|
out := strings.Join(wordList, " ")
|
|
fmt.Print(out)
|
|
memory.Set(outputPrintedFlag, -1)
|
|
}
|
|
|
|
func showSubroutine() {
|
|
name, err := scanner.Read()
|
|
if err != nil {
|
|
handleError(fmt.Errorf("show subroutine panic"))
|
|
return
|
|
}
|
|
nameString := name.String()
|
|
if val, ok := names[nameString]; ok {
|
|
fmt.Printf("\033[3m%s\033[23m ", buildWordDefinition(nameString, val))
|
|
} else if _, ok := builtins[nameString]; ok {
|
|
fmt.Printf("\033[3m%s is a built-in\033[23m ", name)
|
|
} else {
|
|
fmt.Printf("\033[31m%s is not a known word\033[0m ", name)
|
|
}
|
|
memory.Set(outputPrintedFlag, -1)
|
|
}
|
|
|
|
func do() {
|
|
if memory.Get(actionModeFlag) == 0 {
|
|
handleError(fmt.Errorf("The word %q was found outside of a subroutine body", "do"))
|
|
return
|
|
} else {
|
|
memory.Put(actionLoopCounter, &address)
|
|
}
|
|
}
|
|
|
|
func loop() {
|
|
if memory.Get(actionModeFlag) == 0 {
|
|
handleError(fmt.Errorf("The word %q was found outside of a subroutine body", "loop"))
|
|
return
|
|
} else {
|
|
truthiness := data.Pop()
|
|
if truthiness != 0 {
|
|
// Conditional is still true: loop
|
|
memory.Set(actionLoopCounter, address.Peep())
|
|
memory.Set(loopSetFlag, -1) // Set the loop return flag
|
|
} else {
|
|
// Conditional is false: break the looping
|
|
address.Pop()
|
|
memory.Set(loopSetFlag, 0) // Set the loop return flag
|
|
}
|
|
}
|
|
}
|
|
|
|
// Starts an if branch
|
|
func nimfIf() {
|
|
if memory.Get(actionModeFlag) == 0 {
|
|
handleError(fmt.Errorf("The word %q was found outside of a subroutine body", "if"))
|
|
return
|
|
} else {
|
|
// Increment depth
|
|
memory.Set(conditionalDepthCounter, memory.Get(conditionalDepthCounter)+1)
|
|
|
|
// Determine current truthiness of the condition
|
|
truthiness := data.Peep()
|
|
if truthiness != 0 {
|
|
truthiness = -1
|
|
}
|
|
|
|
// If currently set to run pop the data stack
|
|
// else do not pop (like you would for a 'live' if block
|
|
if memory.ValueEquals(conditionalBranchRunFlag, -1) {
|
|
data.Pop()
|
|
}
|
|
|
|
// Push the run state flag to the address stack
|
|
// TODO check on this, Im not sure it is needed
|
|
memory.Put(conditionalBranchRunFlag, &address)
|
|
|
|
// If run state is false push 0
|
|
if memory.ValueEquals(conditionalBranchRunFlag, 0) {
|
|
// This ensures that dead else branches never run
|
|
address.Push(-1)
|
|
} else {
|
|
memory.Set(conditionalBranchRunFlag, truthiness)
|
|
address.Push(truthiness)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Starts the else in an if
|
|
func nimfElse() {
|
|
if memory.Get(actionModeFlag) == 0 {
|
|
handleError(fmt.Errorf("The word %q was found outside of a subroutine body", "else"))
|
|
return
|
|
} else if memory.Get(conditionalDepthCounter) < 1 {
|
|
handleError(fmt.Errorf("The word %q was found (without a preceeding %q)", "else", "if"))
|
|
return
|
|
} else {
|
|
truthiness := address.Peep()
|
|
if truthiness == 0 {
|
|
memory.Set(conditionalBranchRunFlag, -1)
|
|
} else {
|
|
memory.Set(conditionalBranchRunFlag, 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ends a conditional
|
|
func nimfThen() {
|
|
if memory.Get(actionModeFlag) == 0 {
|
|
handleError(fmt.Errorf("The word %q was found outside of a subroutine body", "then"))
|
|
return
|
|
} else if memory.Get(conditionalDepthCounter) < 1 {
|
|
handleError(fmt.Errorf("The word %q was found (without a preceeding %q)", "then", "if"))
|
|
return
|
|
} else {
|
|
address.Pop()
|
|
memory.Set(conditionalDepthCounter, memory.Get(conditionalDepthCounter)-1)
|
|
priorRunState := address.Pop()
|
|
if memory.ValueEquals(conditionalDepthCounter, 0) {
|
|
memory.Set(conditionalBranchRunFlag, -1)
|
|
} else {
|
|
memory.Set(conditionalBranchRunFlag, priorRunState)
|
|
}
|
|
}
|
|
}
|
|
|
|
func winsize() {
|
|
col, row := termios.GetWindowSize()
|
|
data.Push(col)
|
|
data.Push(row)
|
|
}
|
|
|
|
func getEnv() {
|
|
address := data.Pop()
|
|
str, err := nimfStringToGolang(address)
|
|
if err != nil {
|
|
data.Push(0)
|
|
return
|
|
}
|
|
val, ok := os.LookupEnv(str)
|
|
if !ok {
|
|
data.Push(0)
|
|
return
|
|
}
|
|
golangStringToNimf(val)
|
|
data.Push(-1)
|
|
}
|
|
|
|
func exitWord() {
|
|
if memory.Get(actionModeFlag) == 0 {
|
|
handleError(fmt.Errorf("The word %q was found outside of a subroutine body", "exit"))
|
|
return
|
|
} else {
|
|
memory.Set(exitFlag, -1)
|
|
}
|
|
}
|
|
|
|
//==================
|
|
// MEMORY
|
|
//==================
|
|
|
|
func variable() {
|
|
memory.Set(varAssignmentFlag, -1)
|
|
}
|
|
|
|
func stringVariable() {
|
|
memory.Set(svarAssignmentFlag, -1)
|
|
}
|
|
|
|
func writeToMemory() {
|
|
location := data.Pop()
|
|
value := data.Pop()
|
|
memory.Set(location, value)
|
|
}
|
|
|
|
func readFromMemory() {
|
|
memory.Put(data.Pop(), &data)
|
|
}
|
|
|
|
func allotMemory() {
|
|
memory.Allocate(data.Pop())
|
|
}
|
|
|
|
func localAllotMemory() {
|
|
amt := data.Pop()
|
|
memory.lpointer += amt
|
|
}
|
|
|
|
func memoryValueAddition() {
|
|
location := data.Pop()
|
|
value := data.Pop()
|
|
current := memory.Get(location)
|
|
memory.Set(location, value+current)
|
|
}
|
|
|
|
func localVar() {
|
|
memory.Set(localAssignmentFlag, -1)
|
|
}
|
|
|
|
func writeStringToMemory() {
|
|
address := data.Pop()
|
|
memory.SetString(address)
|
|
}
|
|
|
|
func readStringIntoTempBuffer() {
|
|
address := data.Pop()
|
|
length := memory.Get(address)
|
|
if length == 0 {
|
|
memory.Set(tempString, 0)
|
|
memory.tspointer = 50
|
|
return
|
|
}
|
|
end := length + address
|
|
start := address + 1
|
|
memory.GetString(start, end)
|
|
}
|
|
|
|
func readRawStringIntoTempBuffer() {
|
|
end := data.Pop()
|
|
start := data.Pop()
|
|
memory.GetString(start, end)
|
|
}
|
|
|
|
//==================
|
|
// IO
|
|
//==================
|
|
|
|
func output() {
|
|
fmt.Printf("%d", data.Pop())
|
|
memory.Set(outputPrintedFlag, -1)
|
|
}
|
|
|
|
func newline() {
|
|
fmt.Print("\n")
|
|
memory.Set(outputPrintedFlag, -1)
|
|
}
|
|
|
|
func space() {
|
|
fmt.Print(" ")
|
|
memory.Set(outputPrintedFlag, -1)
|
|
}
|
|
|
|
func emit() {
|
|
charCode := data.Pop()
|
|
fmt.Printf("%c", charCode)
|
|
memory.Set(outputPrintedFlag, -1)
|
|
}
|
|
|
|
func getKeyPress() {
|
|
termios.SetCharMode()
|
|
reader := bufio.NewReader(os.Stdin)
|
|
char, _, err := reader.ReadRune()
|
|
|
|
if err != nil {
|
|
termios.SetLineMode()
|
|
return
|
|
}
|
|
data.Push(int(char))
|
|
termios.SetLineMode()
|
|
}
|
|
|
|
func bufferedInputRequest() {
|
|
reader := bufio.NewReader(os.Stdin)
|
|
text, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
text = strings.TrimSuffix(text, "\n")
|
|
golangStringToNimf(text)
|
|
}
|
|
|
|
func connect() {
|
|
memLocation := data.Pop()
|
|
host, err := nimfStringToGolang(memLocation)
|
|
if err != nil {
|
|
data.Push(0)
|
|
return
|
|
}
|
|
netConnection, err = net.Dial("tcp", host)
|
|
if err != nil {
|
|
data.Push(0)
|
|
return
|
|
}
|
|
data.Push(-1)
|
|
}
|
|
|
|
func connWrite() {
|
|
memLocation := data.Pop()
|
|
msg, err := nimfStringToGolang(memLocation)
|
|
if err != nil {
|
|
data.Push(0)
|
|
return
|
|
}
|
|
_, err = netConnection.Write([]byte(msg))
|
|
if err != nil {
|
|
data.Push(0)
|
|
return
|
|
}
|
|
data.Push(-1)
|
|
}
|
|
|
|
func connReadAll() {
|
|
result, err := ioutil.ReadAll(netConnection)
|
|
if err != nil {
|
|
data.Push(0)
|
|
return
|
|
}
|
|
|
|
golangStringToNimf(string(result))
|
|
data.Push(-1)
|
|
}
|
|
|
|
func connClose() {
|
|
if netConnection == nil {
|
|
return
|
|
}
|
|
netConnection.Close()
|
|
netConnection = nil
|
|
}
|
|
|
|
func openFile() {
|
|
mode := data.Pop()
|
|
stringAddress := data.Pop()
|
|
if limitIO {
|
|
handleError(fmt.Errorf("Nimf is running in a mode that does not allow file IO"))
|
|
return
|
|
}
|
|
fileName, err := nimfStringToGolang(stringAddress)
|
|
if err != nil {
|
|
handleError(fmt.Errorf("Null path (0 length) provided to file-open"))
|
|
return
|
|
}
|
|
|
|
var modeFlag int
|
|
switch mode {
|
|
case int('r'): // read by line
|
|
modeFlag = os.O_RDONLY
|
|
case int('R'): // read whole file in and close file
|
|
readWholeFile(fileName)
|
|
return
|
|
case int('w'):
|
|
modeFlag = os.O_WRONLY | os.O_CREATE | os.O_TRUNC
|
|
case int('a'):
|
|
modeFlag = os.O_WRONLY | os.O_CREATE | os.O_APPEND
|
|
}
|
|
|
|
file, err = os.OpenFile(fileName, modeFlag, 0644)
|
|
if err != nil {
|
|
data.Push(0)
|
|
handleError(err)
|
|
return
|
|
}
|
|
if mode == int('r') {
|
|
fileReader = bufio.NewReader(file)
|
|
}
|
|
}
|
|
|
|
func fileExists() {
|
|
stringAddress := data.Pop()
|
|
if limitIO {
|
|
handleError(fmt.Errorf("Nimf is running in a mode that does not allow file IO"))
|
|
return
|
|
}
|
|
str, err := nimfStringToGolang(stringAddress)
|
|
if err != nil {
|
|
handleError(fmt.Errorf("Invalid string provided to file-exists?"))
|
|
return
|
|
}
|
|
_, err = os.Stat(str)
|
|
if err != nil {
|
|
data.Push(0)
|
|
} else {
|
|
data.Push(-1)
|
|
}
|
|
}
|
|
|
|
func readWholeFile(fileName string) {
|
|
b, err := ioutil.ReadFile(fileName)
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
text := string(b)
|
|
length := len(text)
|
|
if length > 29999-51 {
|
|
text = text[:29999-51]
|
|
}
|
|
golangStringToNimf(text)
|
|
}
|
|
|
|
func readLine() {
|
|
line, err := fileReader.ReadString('\n')
|
|
if err == io.EOF {
|
|
data.Push(0)
|
|
} else if err != nil {
|
|
handleError(err)
|
|
return
|
|
} else {
|
|
golangStringToNimf(line)
|
|
data.Push(-1)
|
|
}
|
|
}
|
|
|
|
func writeToFile() {
|
|
stringAddress := data.Pop()
|
|
str, err := nimfStringToGolang(stringAddress)
|
|
if err != nil {
|
|
handleError(fmt.Errorf("Invalid string provided to file-write"))
|
|
return
|
|
}
|
|
_, err = fmt.Fprint(file, str)
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func closeFile() {
|
|
if file == nil {
|
|
return
|
|
}
|
|
err := file.Close()
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
file = nil
|
|
}
|
|
|
|
func addSubprocess() {
|
|
address := data.Pop()
|
|
if limitIO {
|
|
handleError(fmt.Errorf("Nimf is running in a mode that does not allow file IO"))
|
|
return
|
|
}
|
|
procString, err := nimfStringToGolang(address)
|
|
if err != nil {
|
|
handleError(fmt.Errorf("Invalid process string provided to subproc.create"))
|
|
return
|
|
}
|
|
procFields := strings.Fields(procString)
|
|
processWrite = false
|
|
if len(procFields) > 1 {
|
|
process = exec.Command(procFields[0], procFields[1:]...)
|
|
} else {
|
|
process = exec.Command(procFields[0])
|
|
}
|
|
}
|
|
|
|
func pipeToSubprocess() {
|
|
address := data.Pop()
|
|
if limitIO {
|
|
handleError(fmt.Errorf("Nimf is running in a mode that does not allow file IO"))
|
|
return
|
|
}
|
|
dataString, err := nimfStringToGolang(address)
|
|
if err != nil {
|
|
handleError(fmt.Errorf("Invalid input string provided to subproc.send"))
|
|
return
|
|
}
|
|
if process == nil {
|
|
handleError(fmt.Errorf("There is not a current process, create one with subproc.create"))
|
|
return
|
|
}
|
|
stdin, err := process.StdinPipe()
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
defer stdin.Close()
|
|
_, err := fmt.Fprint(stdin, dataString)
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
processWrite = true
|
|
}()
|
|
}
|
|
|
|
func runSubprocess() {
|
|
mode := data.Pop()
|
|
if limitIO {
|
|
handleError(fmt.Errorf("Nimf is running in a mode that does not allow file IO"))
|
|
return
|
|
}
|
|
switch mode {
|
|
case int('q'):
|
|
// Quick/Non-Interactive Mode
|
|
out, err := process.CombinedOutput()
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
output := string(out)
|
|
if len(output) > 0 {
|
|
golangStringToNimf(string(out))
|
|
} else {
|
|
memory.Set(50, 0)
|
|
}
|
|
case int('i'):
|
|
// Interactive Mode
|
|
if !processWrite {
|
|
process.Stdin = os.Stdin
|
|
}
|
|
process.Stdout = os.Stdout
|
|
err := process.Run()
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
case int('b'):
|
|
// Background Mode
|
|
err := process.Start()
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
default:
|
|
handleError(fmt.Errorf("Invalid mode sent to subproc.exec"))
|
|
return
|
|
}
|
|
process = nil
|
|
}
|
|
|
|
func procWorkingDir() {
|
|
pathAddress := data.Pop()
|
|
if limitIO {
|
|
handleError(fmt.Errorf("Nimf is running in a mode that does not allow file IO"))
|
|
return
|
|
}
|
|
path, err := nimfStringToGolang(pathAddress)
|
|
if err != nil {
|
|
handleError(fmt.Errorf("Invalid path string provided to subproc.cd"))
|
|
return
|
|
}
|
|
path = expandedAbsFilepath(path)
|
|
process.Dir = path
|
|
}
|
|
|
|
func nilSubprocess() {
|
|
if process == nil {
|
|
data.Push(0)
|
|
} else {
|
|
data.Push(-1)
|
|
}
|
|
}
|
|
|
|
func getPwd() {
|
|
if limitIO {
|
|
handleError(fmt.Errorf("Nimf is running in a mode that does not allow file IO"))
|
|
return
|
|
}
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
golangStringToNimf(wd)
|
|
}
|
|
|
|
func changeDir() {
|
|
if limitIO {
|
|
handleError(fmt.Errorf("Nimf is running in a mode that does not allow file IO"))
|
|
return
|
|
}
|
|
strAddr := data.Pop()
|
|
path, err := nimfStringToGolang(strAddr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = os.Chdir(expandedAbsFilepath(path))
|
|
if err != nil {
|
|
handleError(err)
|
|
return
|
|
}
|
|
}
|