mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
update false platform to purity inference
This commit is contained in:
parent
14a338e15b
commit
f51061b200
9 changed files with 291 additions and 380 deletions
|
@ -1,4 +1,4 @@
|
|||
module [Context, Data, with, getChar, Option, pushStack, popStack, toStr, inWhileScope]
|
||||
module [Context, Data, with!, getChar!, Option, pushStack, popStack, toStr, inWhileScope]
|
||||
|
||||
import pf.File
|
||||
import Variable exposing [Variable]
|
||||
|
@ -20,13 +20,13 @@ pushStack = \ctx, data ->
|
|||
# I think an open tag union should just work here.
|
||||
# Instead at a call sites, I need to match on the error and then return the same error.
|
||||
# Otherwise it hits unreachable code in ir.rs
|
||||
popStack : Context -> Result [T Context Data] [EmptyStack]
|
||||
popStack : Context -> Result (Context, Data) [EmptyStack]
|
||||
popStack = \ctx ->
|
||||
when List.last ctx.stack is
|
||||
Ok val ->
|
||||
poppedCtx = { ctx & stack: List.dropAt ctx.stack (List.len ctx.stack - 1) }
|
||||
|
||||
Ok (T poppedCtx val)
|
||||
Ok (poppedCtx, val)
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Err EmptyStack
|
||||
|
@ -58,30 +58,30 @@ toStr = \{ scopes, stack, state, vars } ->
|
|||
|
||||
"\n============\nDepth: $(depth)\nState: $(stateStr)\nStack: [$(stackStr)]\nVars: [$(varsStr)]\n============\n"
|
||||
|
||||
with : Str, (Context -> Task {} a) -> Task {} a
|
||||
with = \path, callback ->
|
||||
File.withOpen path \handle ->
|
||||
with! : Str, (Context => a) => a
|
||||
with! = \path, callback! ->
|
||||
File.withOpen! path \handle ->
|
||||
# I cant define scope here and put it in the list in callback. It breaks alias anaysis.
|
||||
# Instead I have to inline this.
|
||||
# root_scope = { data: Some handle, index: 0, buf: [], whileInfo: None }
|
||||
callback { scopes: [{ data: Some handle, index: 0, buf: [], whileInfo: None }], state: Executing, stack: [], vars: List.repeat (Number 0) Variable.totalCount }
|
||||
callback! { scopes: [{ data: Some handle, index: 0, buf: [], whileInfo: None }], state: Executing, stack: [], vars: List.repeat (Number 0) Variable.totalCount }
|
||||
|
||||
# I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is.
|
||||
getChar : Context -> Task [T U8 Context] [EndOfData, NoScope]
|
||||
getChar = \ctx ->
|
||||
getChar! : Context => Result (U8, Context) [EndOfData, NoScope]
|
||||
getChar! = \ctx ->
|
||||
when List.last ctx.scopes is
|
||||
Ok scope ->
|
||||
(T val newScope) = getCharScope! scope
|
||||
Task.ok (T val { ctx & scopes: List.set ctx.scopes (List.len ctx.scopes - 1) newScope })
|
||||
(val, newScope) = getCharScope!? scope
|
||||
Ok (val, { ctx & scopes: List.set ctx.scopes (List.len ctx.scopes - 1) newScope })
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
getCharScope : Scope -> Task [T U8 Scope] [EndOfData, NoScope]
|
||||
getCharScope = \scope ->
|
||||
getCharScope! : Scope => Result (U8, Scope) [EndOfData, NoScope]
|
||||
getCharScope! = \scope ->
|
||||
when List.get scope.buf scope.index is
|
||||
Ok val ->
|
||||
Task.ok (T val { scope & index: scope.index + 1 })
|
||||
Ok (val, { scope & index: scope.index + 1 })
|
||||
|
||||
Err OutOfBounds ->
|
||||
when scope.data is
|
||||
|
@ -90,13 +90,13 @@ getCharScope = \scope ->
|
|||
when List.first bytes is
|
||||
Ok val ->
|
||||
# This starts at 1 because the first character is already being returned.
|
||||
Task.ok (T val { scope & buf: bytes, index: 1 })
|
||||
Ok (val, { scope & buf: bytes, index: 1 })
|
||||
|
||||
Err ListWasEmpty ->
|
||||
Task.err EndOfData
|
||||
Err EndOfData
|
||||
|
||||
None ->
|
||||
Task.err EndOfData
|
||||
Err EndOfData
|
||||
|
||||
inWhileScope : Context -> Bool
|
||||
inWhileScope = \ctx ->
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
app [main] { pf: platform "platform/main.roc" }
|
||||
app [main!] { pf: platform "platform/main.roc" }
|
||||
|
||||
import pf.Stdout
|
||||
import pf.Stdin
|
||||
|
@ -20,54 +20,58 @@ import Variable exposing [Variable]
|
|||
# I assume all of the Task.awaits are the cause of this, but I am not 100% sure.
|
||||
InterpreterErrors : [BadUtf8, DivByZero, EmptyStack, InvalidBooleanValue, InvalidChar Str, MaxInputNumber, NoLambdaOnStack, NoNumberOnStack, NoVariableOnStack, NoScope, OutOfBounds, UnexpectedEndOfData]
|
||||
|
||||
main : Str -> Task {} []
|
||||
main = \filename ->
|
||||
interpretFile filename
|
||||
|> Task.onErr \StringErr e -> Stdout.line "Ran into problem:\n$(e)\n"
|
||||
main! : Str => {}
|
||||
main! = \filename ->
|
||||
when interpretFile! filename is
|
||||
Ok {} ->
|
||||
{}
|
||||
|
||||
interpretFile : Str -> Task {} [StringErr Str]
|
||||
interpretFile = \filename ->
|
||||
Context.with filename \ctx ->
|
||||
result = interpretCtx ctx |> Task.result!
|
||||
Err (StringErr e) ->
|
||||
Stdout.line! "Ran into problem:\n$(e)\n"
|
||||
|
||||
interpretFile! : Str => Result {} [StringErr Str]
|
||||
interpretFile! = \filename ->
|
||||
Context.with! filename \ctx ->
|
||||
result = interpretCtx! ctx
|
||||
when result is
|
||||
Ok _ ->
|
||||
Task.ok {}
|
||||
Ok {}
|
||||
|
||||
Err BadUtf8 ->
|
||||
Task.err (StringErr "Failed to convert string from Utf8 bytes")
|
||||
Err (StringErr "Failed to convert string from Utf8 bytes")
|
||||
|
||||
Err DivByZero ->
|
||||
Task.err (StringErr "Division by zero")
|
||||
Err (StringErr "Division by zero")
|
||||
|
||||
Err EmptyStack ->
|
||||
Task.err (StringErr "Tried to pop a value off of the stack when it was empty")
|
||||
Err (StringErr "Tried to pop a value off of the stack when it was empty")
|
||||
|
||||
Err InvalidBooleanValue ->
|
||||
Task.err (StringErr "Ran into an invalid boolean that was neither false (0) or true (-1)")
|
||||
Err (StringErr "Ran into an invalid boolean that was neither false (0) or true (-1)")
|
||||
|
||||
Err (InvalidChar char) ->
|
||||
Task.err (StringErr "Ran into an invalid character with ascii code: $(char)")
|
||||
Err (StringErr "Ran into an invalid character with ascii code: $(char)")
|
||||
|
||||
Err MaxInputNumber ->
|
||||
Task.err (StringErr "Like the original false compiler, the max input number is 320,000")
|
||||
Err (StringErr "Like the original false compiler, the max input number is 320,000")
|
||||
|
||||
Err NoLambdaOnStack ->
|
||||
Task.err (StringErr "Tried to run a lambda when no lambda was on the stack")
|
||||
Err (StringErr "Tried to run a lambda when no lambda was on the stack")
|
||||
|
||||
Err NoNumberOnStack ->
|
||||
Task.err (StringErr "Tried to run a number when no number was on the stack")
|
||||
Err (StringErr "Tried to run a number when no number was on the stack")
|
||||
|
||||
Err NoVariableOnStack ->
|
||||
Task.err (StringErr "Tried to load a variable when no variable was on the stack")
|
||||
Err (StringErr "Tried to load a variable when no variable was on the stack")
|
||||
|
||||
Err NoScope ->
|
||||
Task.err (StringErr "Tried to run code when not in any scope")
|
||||
Err (StringErr "Tried to run code when not in any scope")
|
||||
|
||||
Err OutOfBounds ->
|
||||
Task.err (StringErr "Tried to load from an offset that was outside of the stack")
|
||||
Err (StringErr "Tried to load from an offset that was outside of the stack")
|
||||
|
||||
Err UnexpectedEndOfData ->
|
||||
Task.err (StringErr "Hit end of data while still parsing something")
|
||||
Err (StringErr "Hit end of data while still parsing something")
|
||||
|
||||
isDigit : U8 -> Bool
|
||||
isDigit = \char ->
|
||||
|
@ -75,6 +79,7 @@ isDigit = \char ->
|
|||
>= 0x30 # `0`
|
||||
&& char
|
||||
<= 0x39 # `0`
|
||||
|
||||
isWhitespace : U8 -> Bool
|
||||
isWhitespace = \char ->
|
||||
char
|
||||
|
@ -85,12 +90,21 @@ isWhitespace = \char ->
|
|||
== 0x20 # space
|
||||
|| char
|
||||
== 0x9 # tab
|
||||
interpretCtx : Context -> Task Context InterpreterErrors
|
||||
interpretCtx = \ctx ->
|
||||
Task.loop ctx interpretCtxLoop
|
||||
|
||||
interpretCtxLoop : Context -> Task [Step Context, Done Context] InterpreterErrors
|
||||
interpretCtxLoop = \ctx ->
|
||||
interpretCtx! : Context => Result Context InterpreterErrors
|
||||
interpretCtx! = \ctx ->
|
||||
when interpretCtxLoop! ctx is
|
||||
Ok (Step next) ->
|
||||
interpretCtx! next
|
||||
|
||||
Ok (Done next) ->
|
||||
Ok next
|
||||
|
||||
Err e ->
|
||||
Err e
|
||||
|
||||
interpretCtxLoop! : Context => Result [Step Context, Done Context] InterpreterErrors
|
||||
interpretCtxLoop! = \ctx ->
|
||||
when ctx.state is
|
||||
Executing if Context.inWhileScope ctx ->
|
||||
# Deal with the current while loop potentially looping.
|
||||
|
@ -102,41 +116,41 @@ interpretCtxLoop = \ctx ->
|
|||
Some { state: InCond, body, cond } ->
|
||||
# Just ran condition. Check the top of stack to see if body should run.
|
||||
when popNumber ctx is
|
||||
Ok (T popCtx n) ->
|
||||
Ok (popCtx, n) ->
|
||||
if n == 0 then
|
||||
newScope = { scope & whileInfo: None }
|
||||
|
||||
Task.ok (Step { popCtx & scopes: List.set ctx.scopes last newScope })
|
||||
Ok (Step { popCtx & scopes: List.set ctx.scopes last newScope })
|
||||
else
|
||||
newScope = { scope & whileInfo: Some { state: InBody, body, cond } }
|
||||
|
||||
Task.ok (Step { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } })
|
||||
Ok (Step { popCtx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: body, index: 0, whileInfo: None } })
|
||||
|
||||
Err e ->
|
||||
Task.err e
|
||||
Err e
|
||||
|
||||
Some { state: InBody, body, cond } ->
|
||||
# Just rand the body. Run the condition again.
|
||||
newScope = { scope & whileInfo: Some { state: InCond, body, cond } }
|
||||
|
||||
Task.ok (Step { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } })
|
||||
Ok (Step { ctx & scopes: List.append (List.set ctx.scopes last newScope) { data: None, buf: cond, index: 0, whileInfo: None } })
|
||||
|
||||
None ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Err OutOfBounds ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Executing ->
|
||||
# Stdout.line! (Context.toStr ctx)
|
||||
result = Context.getChar ctx |> Task.result!
|
||||
result = Context.getChar! ctx
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
execCtx = stepExecCtx! newCtx val
|
||||
Task.ok (Step execCtx)
|
||||
Ok (val, newCtx) ->
|
||||
execCtx = stepExecCtx!? newCtx val
|
||||
Ok (Step execCtx)
|
||||
|
||||
Err NoScope ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
# Computation complete for this scope.
|
||||
|
@ -145,30 +159,30 @@ interpretCtxLoop = \ctx ->
|
|||
|
||||
# If no scopes left, all execution complete.
|
||||
if List.isEmpty dropCtx.scopes then
|
||||
Task.ok (Done dropCtx)
|
||||
Ok (Done dropCtx)
|
||||
else
|
||||
Task.ok (Step dropCtx)
|
||||
Ok (Step dropCtx)
|
||||
|
||||
InComment ->
|
||||
result = Context.getChar ctx |> Task.result!
|
||||
result = Context.getChar! ctx
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
Ok (val, newCtx) ->
|
||||
if val == 0x7D then
|
||||
# `}` end of comment
|
||||
Task.ok (Step { newCtx & state: Executing })
|
||||
Ok (Step { newCtx & state: Executing })
|
||||
else
|
||||
Task.ok (Step { newCtx & state: InComment })
|
||||
Ok (Step { newCtx & state: InComment })
|
||||
|
||||
Err NoScope ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.err UnexpectedEndOfData
|
||||
Err UnexpectedEndOfData
|
||||
|
||||
InNumber accum ->
|
||||
result = Context.getChar ctx |> Task.result!
|
||||
result = Context.getChar! ctx
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
Ok (val, newCtx) ->
|
||||
if isDigit val then
|
||||
# still in the number
|
||||
# i32 multiplication is kinda broken because it implicitly seems to want to upcast to i64.
|
||||
|
@ -176,72 +190,72 @@ interpretCtxLoop = \ctx ->
|
|||
# so this is make i64 mul by 10 then convert back to i32.
|
||||
nextAccum = (10 * Num.intCast accum) + Num.intCast (val - 0x30)
|
||||
|
||||
Task.ok (Step { newCtx & state: InNumber (Num.intCast nextAccum) })
|
||||
Ok (Step { newCtx & state: InNumber (Num.intCast nextAccum) })
|
||||
else
|
||||
# outside of number now, this needs to be executed.
|
||||
pushCtx = Context.pushStack newCtx (Number accum)
|
||||
|
||||
execCtx = stepExecCtx! { pushCtx & state: Executing } val
|
||||
Task.ok (Step execCtx)
|
||||
execCtx = stepExecCtx!? { pushCtx & state: Executing } val
|
||||
Ok (Step execCtx)
|
||||
|
||||
Err NoScope ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.err UnexpectedEndOfData
|
||||
Err UnexpectedEndOfData
|
||||
|
||||
InString bytes ->
|
||||
result = Context.getChar ctx |> Task.result!
|
||||
result = Context.getChar! ctx
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
Ok (val, newCtx) ->
|
||||
if val == 0x22 then
|
||||
# `"` end of string
|
||||
when Str.fromUtf8 bytes is
|
||||
Ok str ->
|
||||
Stdout.raw! str
|
||||
Task.ok (Step { newCtx & state: Executing })
|
||||
Ok (Step { newCtx & state: Executing })
|
||||
|
||||
Err _ ->
|
||||
Task.err BadUtf8
|
||||
Err BadUtf8
|
||||
else
|
||||
Task.ok (Step { newCtx & state: InString (List.append bytes val) })
|
||||
Ok (Step { newCtx & state: InString (List.append bytes val) })
|
||||
|
||||
Err NoScope ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.err UnexpectedEndOfData
|
||||
Err UnexpectedEndOfData
|
||||
|
||||
InLambda depth bytes ->
|
||||
result = Context.getChar ctx |> Task.result!
|
||||
result = Context.getChar! ctx
|
||||
when result is
|
||||
Ok (T val newCtx) ->
|
||||
Ok (val, newCtx) ->
|
||||
if val == 0x5B then
|
||||
# start of a nested lambda `[`
|
||||
Task.ok (Step { newCtx & state: InLambda (depth + 1) (List.append bytes val) })
|
||||
Ok (Step { newCtx & state: InLambda (depth + 1) (List.append bytes val) })
|
||||
else if val == 0x5D then
|
||||
# `]` end of current lambda
|
||||
if depth == 0 then
|
||||
# end of all lambdas
|
||||
Task.ok (Step (Context.pushStack { newCtx & state: Executing } (Lambda bytes)))
|
||||
Ok (Step (Context.pushStack { newCtx & state: Executing } (Lambda bytes)))
|
||||
else
|
||||
# end of nested lambda
|
||||
Task.ok (Step { newCtx & state: InLambda (depth - 1) (List.append bytes val) })
|
||||
Ok (Step { newCtx & state: InLambda (depth - 1) (List.append bytes val) })
|
||||
else
|
||||
Task.ok (Step { newCtx & state: InLambda depth (List.append bytes val) })
|
||||
Ok (Step { newCtx & state: InLambda depth (List.append bytes val) })
|
||||
|
||||
Err NoScope ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.err UnexpectedEndOfData
|
||||
Err UnexpectedEndOfData
|
||||
|
||||
InSpecialChar ->
|
||||
result = Context.getChar { ctx & state: Executing } |> Task.result!
|
||||
result = Context.getChar! { ctx & state: Executing }
|
||||
when result is
|
||||
Ok (T 0xB8 newCtx) ->
|
||||
Ok (0xB8, newCtx) ->
|
||||
result2 =
|
||||
(T popCtx index) = popNumber? newCtx
|
||||
(popCtx, index) = popNumber? newCtx
|
||||
# I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers.
|
||||
size = List.len popCtx.stack - 1
|
||||
offset = Num.intCast size - index
|
||||
|
@ -253,329 +267,302 @@ interpretCtxLoop = \ctx ->
|
|||
Err OutOfBounds
|
||||
|
||||
when result2 is
|
||||
Ok a -> Task.ok (Step a)
|
||||
Err e -> Task.err e
|
||||
Ok a -> Ok (Step a)
|
||||
Err e -> Err e
|
||||
|
||||
Ok (T 0x9F newCtx) ->
|
||||
Ok (0x9F, newCtx) ->
|
||||
# This is supposed to flush io buffers. We don't buffer, so it does nothing
|
||||
Task.ok (Step newCtx)
|
||||
Ok (Step newCtx)
|
||||
|
||||
Ok (T x _) ->
|
||||
Ok (x, _) ->
|
||||
data = Num.toStr (Num.intCast x)
|
||||
|
||||
Task.err (InvalidChar data)
|
||||
Err (InvalidChar data)
|
||||
|
||||
Err NoScope ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.err UnexpectedEndOfData
|
||||
Err UnexpectedEndOfData
|
||||
|
||||
LoadChar ->
|
||||
result = Context.getChar { ctx & state: Executing } |> Task.result!
|
||||
result = Context.getChar! { ctx & state: Executing }
|
||||
when result is
|
||||
Ok (T x newCtx) ->
|
||||
Task.ok (Step (Context.pushStack newCtx (Number (Num.intCast x))))
|
||||
Ok (x, newCtx) ->
|
||||
Ok (Step (Context.pushStack newCtx (Number (Num.intCast x))))
|
||||
|
||||
Err NoScope ->
|
||||
Task.err NoScope
|
||||
Err NoScope
|
||||
|
||||
Err EndOfData ->
|
||||
Task.err UnexpectedEndOfData
|
||||
Err UnexpectedEndOfData
|
||||
|
||||
# If it weren't for reading stdin or writing to stdout, this could return a result.
|
||||
stepExecCtx : Context, U8 -> Task Context InterpreterErrors
|
||||
stepExecCtx = \ctx, char ->
|
||||
stepExecCtx! : Context, U8 => Result Context InterpreterErrors
|
||||
stepExecCtx! = \ctx, char ->
|
||||
when char is
|
||||
0x21 ->
|
||||
# `!` execute lambda
|
||||
Task.fromResult
|
||||
(
|
||||
(T popCtx bytes) = popLambda? ctx
|
||||
Ok { popCtx & scopes: List.append popCtx.scopes { data: None, buf: bytes, index: 0, whileInfo: None } }
|
||||
)
|
||||
(popCtx, bytes) = popLambda? ctx
|
||||
Ok { popCtx & scopes: List.append popCtx.scopes { data: None, buf: bytes, index: 0, whileInfo: None } }
|
||||
|
||||
0x3F ->
|
||||
# `?` if
|
||||
Task.fromResult
|
||||
(
|
||||
(T popCtx1 bytes) = popLambda? ctx
|
||||
(T popCtx2 n1) = popNumber? popCtx1
|
||||
if n1 == 0 then
|
||||
Ok popCtx2
|
||||
else
|
||||
Ok { popCtx2 & scopes: List.append popCtx2.scopes { data: None, buf: bytes, index: 0, whileInfo: None } }
|
||||
)
|
||||
(popCtx1, bytes) = popLambda? ctx
|
||||
(popCtx2, n1) = popNumber? popCtx1
|
||||
if n1 == 0 then
|
||||
Ok popCtx2
|
||||
else
|
||||
Ok { popCtx2 & scopes: List.append popCtx2.scopes { data: None, buf: bytes, index: 0, whileInfo: None } }
|
||||
|
||||
0x23 ->
|
||||
# `#` while
|
||||
Task.fromResult
|
||||
(
|
||||
(T popCtx1 body) = popLambda? ctx
|
||||
(T popCtx2 cond) = popLambda? popCtx1
|
||||
last = (List.len popCtx2.scopes - 1)
|
||||
(popCtx1, body) = popLambda? ctx
|
||||
(popCtx2, cond) = popLambda? popCtx1
|
||||
last = (List.len popCtx2.scopes - 1)
|
||||
|
||||
when List.get popCtx2.scopes last is
|
||||
Ok scope ->
|
||||
# set the current scope to be in a while loop.
|
||||
scopes = List.set popCtx2.scopes last { scope & whileInfo: Some { cond: cond, body: body, state: InCond } }
|
||||
when List.get popCtx2.scopes last is
|
||||
Ok scope ->
|
||||
# set the current scope to be in a while loop.
|
||||
scopes = List.set popCtx2.scopes last { scope & whileInfo: Some { cond: cond, body: body, state: InCond } }
|
||||
|
||||
# push a scope to execute the condition.
|
||||
Ok { popCtx2 & scopes: List.append scopes { data: None, buf: cond, index: 0, whileInfo: None } }
|
||||
# push a scope to execute the condition.
|
||||
Ok { popCtx2 & scopes: List.append scopes { data: None, buf: cond, index: 0, whileInfo: None } }
|
||||
|
||||
Err OutOfBounds ->
|
||||
Err NoScope
|
||||
)
|
||||
Err OutOfBounds ->
|
||||
Err NoScope
|
||||
|
||||
0x24 ->
|
||||
# `$` dup
|
||||
# Switching this to List.last and changing the error to ListWasEmpty leads to a compiler bug.
|
||||
# Complains about the types eq not matching.
|
||||
when List.get ctx.stack (List.len ctx.stack - 1) is
|
||||
Ok dupItem -> Task.ok (Context.pushStack ctx dupItem)
|
||||
Err OutOfBounds -> Task.err EmptyStack
|
||||
Ok dupItem -> Ok (Context.pushStack ctx dupItem)
|
||||
Err OutOfBounds -> Err EmptyStack
|
||||
|
||||
0x25 ->
|
||||
# `%` drop
|
||||
when Context.popStack ctx is
|
||||
# Dropping with an empty stack, all results here are fine
|
||||
Ok (T popCtx _) -> Task.ok popCtx
|
||||
Err _ -> Task.ok ctx
|
||||
Ok (popCtx, _) -> Ok popCtx
|
||||
Err _ -> Ok ctx
|
||||
|
||||
0x5C ->
|
||||
# `\` swap
|
||||
result2 =
|
||||
(T popCtx1 n1) = Context.popStack? ctx
|
||||
(T popCtx2 n2) = Context.popStack? popCtx1
|
||||
(popCtx1, n1) = Context.popStack? ctx
|
||||
(popCtx2, n2) = Context.popStack? popCtx1
|
||||
Ok (Context.pushStack (Context.pushStack popCtx2 n1) n2)
|
||||
|
||||
when result2 is
|
||||
Ok a ->
|
||||
Task.ok a
|
||||
Ok a
|
||||
|
||||
# Being explicit with error type is required to stop the need to propogate the error parameters to Context.popStack
|
||||
Err EmptyStack ->
|
||||
Task.err EmptyStack
|
||||
Err EmptyStack
|
||||
|
||||
0x40 ->
|
||||
# `@` rot
|
||||
result2 =
|
||||
(T popCtx1 n1) = Context.popStack? ctx
|
||||
(T popCtx2 n2) = Context.popStack? popCtx1
|
||||
(T popCtx3 n3) = Context.popStack? popCtx2
|
||||
(popCtx1, n1) = Context.popStack? ctx
|
||||
(popCtx2, n2) = Context.popStack? popCtx1
|
||||
(popCtx3, n3) = Context.popStack? popCtx2
|
||||
Ok (Context.pushStack (Context.pushStack (Context.pushStack popCtx3 n2) n1) n3)
|
||||
|
||||
when result2 is
|
||||
Ok a ->
|
||||
Task.ok a
|
||||
Ok a
|
||||
|
||||
# Being explicit with error type is required to stop the need to propogate the error parameters to Context.popStack
|
||||
Err EmptyStack ->
|
||||
Task.err EmptyStack
|
||||
Err EmptyStack
|
||||
|
||||
0xC3 ->
|
||||
# `ø` pick or `ß` flush
|
||||
# these are actually 2 bytes, 0xC3 0xB8 or 0xC3 0x9F
|
||||
# requires special parsing
|
||||
Task.ok { ctx & state: InSpecialChar }
|
||||
Ok { ctx & state: InSpecialChar }
|
||||
|
||||
0x4F ->
|
||||
# `O` also treat this as pick for easier script writing
|
||||
Task.fromResult
|
||||
(
|
||||
(T popCtx index) = popNumber? ctx
|
||||
# I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers.
|
||||
size = List.len popCtx.stack - 1
|
||||
offset = Num.intCast size - index
|
||||
(popCtx, index) = popNumber? ctx
|
||||
# I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers.
|
||||
size = List.len popCtx.stack - 1
|
||||
offset = Num.intCast size - index
|
||||
|
||||
if offset >= 0 then
|
||||
stackVal = List.get? popCtx.stack (Num.intCast offset)
|
||||
Ok (Context.pushStack popCtx stackVal)
|
||||
else
|
||||
Err OutOfBounds
|
||||
)
|
||||
if offset >= 0 then
|
||||
stackVal = List.get? popCtx.stack (Num.intCast offset)
|
||||
Ok (Context.pushStack popCtx stackVal)
|
||||
else
|
||||
Err OutOfBounds
|
||||
|
||||
0x42 ->
|
||||
# `B` also treat this as flush for easier script writing
|
||||
# This is supposed to flush io buffers. We don't buffer, so it does nothing
|
||||
Task.ok ctx
|
||||
Ok ctx
|
||||
|
||||
0x27 ->
|
||||
# `'` load next char
|
||||
Task.ok { ctx & state: LoadChar }
|
||||
Ok { ctx & state: LoadChar }
|
||||
|
||||
0x2B ->
|
||||
# `+` add
|
||||
Task.fromResult (binaryOp ctx Num.addWrap)
|
||||
binaryOp ctx Num.addWrap
|
||||
|
||||
0x2D ->
|
||||
# `-` sub
|
||||
Task.fromResult (binaryOp ctx Num.subWrap)
|
||||
binaryOp ctx Num.subWrap
|
||||
|
||||
0x2A ->
|
||||
# `*` mul
|
||||
Task.fromResult (binaryOp ctx Num.mulWrap)
|
||||
binaryOp ctx Num.mulWrap
|
||||
|
||||
0x2F ->
|
||||
# `/` div
|
||||
# Due to possible division by zero error, this must be handled specially.
|
||||
Task.fromResult
|
||||
(
|
||||
(T popCtx1 numR) = popNumber? ctx
|
||||
(T popCtx2 numL) = popNumber? popCtx1
|
||||
res = Num.divTruncChecked? numL numR
|
||||
Ok (Context.pushStack popCtx2 (Number res))
|
||||
)
|
||||
(popCtx1, numR) = popNumber? ctx
|
||||
(popCtx2, numL) = popNumber? popCtx1
|
||||
res = Num.divTruncChecked? numL numR
|
||||
Ok (Context.pushStack popCtx2 (Number res))
|
||||
|
||||
0x26 ->
|
||||
# `&` bitwise and
|
||||
Task.fromResult (binaryOp ctx Num.bitwiseAnd)
|
||||
binaryOp ctx Num.bitwiseAnd
|
||||
|
||||
0x7C ->
|
||||
# `|` bitwise or
|
||||
Task.fromResult (binaryOp ctx Num.bitwiseOr)
|
||||
binaryOp ctx Num.bitwiseOr
|
||||
|
||||
0x3D ->
|
||||
# `=` equals
|
||||
Task.fromResult
|
||||
(
|
||||
binaryOp ctx \a, b ->
|
||||
if a == b then
|
||||
-1
|
||||
else
|
||||
0
|
||||
)
|
||||
binaryOp ctx \a, b ->
|
||||
if a == b then
|
||||
-1
|
||||
else
|
||||
0
|
||||
|
||||
0x3E ->
|
||||
# `>` greater than
|
||||
Task.fromResult
|
||||
(
|
||||
binaryOp ctx \a, b ->
|
||||
if a > b then
|
||||
-1
|
||||
else
|
||||
0
|
||||
)
|
||||
binaryOp ctx \a, b ->
|
||||
if a > b then
|
||||
-1
|
||||
else
|
||||
0
|
||||
|
||||
0x5F ->
|
||||
# `_` negate
|
||||
Task.fromResult (unaryOp ctx Num.neg)
|
||||
unaryOp ctx Num.neg
|
||||
|
||||
0x7E ->
|
||||
# `~` bitwise not
|
||||
Task.fromResult (unaryOp ctx (\x -> Num.bitwiseXor x -1)) # xor with -1 should be bitwise not
|
||||
unaryOp ctx (\x -> Num.bitwiseXor x -1) # xor with -1 should be bitwise not
|
||||
|
||||
0x2C ->
|
||||
# `,` write char
|
||||
when popNumber ctx is
|
||||
Ok (T popCtx num) ->
|
||||
Ok (popCtx, num) ->
|
||||
when Str.fromUtf8 [Num.intCast num] is
|
||||
Ok str ->
|
||||
Stdout.raw! str
|
||||
Task.ok popCtx
|
||||
Ok popCtx
|
||||
|
||||
Err _ ->
|
||||
Task.err BadUtf8
|
||||
Err BadUtf8
|
||||
|
||||
Err e ->
|
||||
Task.err e
|
||||
Err e
|
||||
|
||||
0x2E ->
|
||||
# `.` write int
|
||||
when popNumber ctx is
|
||||
Ok (T popCtx num) ->
|
||||
Ok (popCtx, num) ->
|
||||
Stdout.raw! (Num.toStr (Num.intCast num))
|
||||
Task.ok popCtx
|
||||
Ok popCtx
|
||||
|
||||
Err e ->
|
||||
Task.err e
|
||||
Err e
|
||||
|
||||
0x5E ->
|
||||
# `^` read char as int
|
||||
in = Stdin.char! {}
|
||||
if in == 255 then
|
||||
# max char sent on EOF. Change to -1
|
||||
Task.ok (Context.pushStack ctx (Number -1))
|
||||
Ok (Context.pushStack ctx (Number -1))
|
||||
else
|
||||
Task.ok (Context.pushStack ctx (Number (Num.intCast in)))
|
||||
Ok (Context.pushStack ctx (Number (Num.intCast in)))
|
||||
|
||||
0x3A ->
|
||||
# `:` store to variable
|
||||
Task.fromResult
|
||||
(
|
||||
(T popCtx1 var) = popVariable? ctx
|
||||
# The Result.mapErr on the next line maps from EmptyStack in Context.roc to the full InterpreterErrors union here.
|
||||
(T popCtx2 n1) = Result.mapErr? (Context.popStack popCtx1) (\EmptyStack -> EmptyStack)
|
||||
Ok { popCtx2 & vars: List.set popCtx2.vars (Variable.toIndex var) n1 }
|
||||
)
|
||||
(popCtx1, var) = popVariable? ctx
|
||||
# The Result.mapErr on the next line maps from EmptyStack in Context.roc to the full InterpreterErrors union here.
|
||||
(popCtx2, n1) = Result.mapErr? (Context.popStack popCtx1) (\EmptyStack -> EmptyStack)
|
||||
Ok { popCtx2 & vars: List.set popCtx2.vars (Variable.toIndex var) n1 }
|
||||
|
||||
0x3B ->
|
||||
# `;` load from variable
|
||||
Task.fromResult
|
||||
(
|
||||
(T popCtx var) = popVariable? ctx
|
||||
elem = List.get? popCtx.vars (Variable.toIndex var)
|
||||
Ok (Context.pushStack popCtx elem)
|
||||
)
|
||||
(popCtx, var) = popVariable? ctx
|
||||
elem = List.get? popCtx.vars (Variable.toIndex var)
|
||||
Ok (Context.pushStack popCtx elem)
|
||||
|
||||
0x22 ->
|
||||
# `"` string start
|
||||
Task.ok { ctx & state: InString [] }
|
||||
Ok { ctx & state: InString [] }
|
||||
|
||||
0x5B ->
|
||||
# `"` string start
|
||||
Task.ok { ctx & state: InLambda 0 [] }
|
||||
Ok { ctx & state: InLambda 0 [] }
|
||||
|
||||
0x7B ->
|
||||
# `{` comment start
|
||||
Task.ok { ctx & state: InComment }
|
||||
Ok { ctx & state: InComment }
|
||||
|
||||
x if isDigit x ->
|
||||
# number start
|
||||
Task.ok { ctx & state: InNumber (Num.intCast (x - 0x30)) }
|
||||
Ok { ctx & state: InNumber (Num.intCast (x - 0x30)) }
|
||||
|
||||
x if isWhitespace x ->
|
||||
Task.ok ctx
|
||||
Ok ctx
|
||||
|
||||
x ->
|
||||
when Variable.fromUtf8 x is
|
||||
# letters are variable names
|
||||
Ok var ->
|
||||
Task.ok (Context.pushStack ctx (Var var))
|
||||
Ok (Context.pushStack ctx (Var var))
|
||||
|
||||
Err _ ->
|
||||
data = Num.toStr (Num.intCast x)
|
||||
|
||||
Task.err (InvalidChar data)
|
||||
Err (InvalidChar data)
|
||||
|
||||
unaryOp : Context, (I32 -> I32) -> Result Context InterpreterErrors
|
||||
unaryOp = \ctx, op ->
|
||||
(T popCtx num) = popNumber? ctx
|
||||
(popCtx, num) = popNumber? ctx
|
||||
Ok (Context.pushStack popCtx (Number (op num)))
|
||||
|
||||
binaryOp : Context, (I32, I32 -> I32) -> Result Context InterpreterErrors
|
||||
binaryOp = \ctx, op ->
|
||||
(T popCtx1 numR) = popNumber? ctx
|
||||
(T popCtx2 numL) = popNumber? popCtx1
|
||||
(popCtx1, numR) = popNumber? ctx
|
||||
(popCtx2, numL) = popNumber? popCtx1
|
||||
Ok (Context.pushStack popCtx2 (Number (op numL numR)))
|
||||
|
||||
popNumber : Context -> Result [T Context I32] InterpreterErrors
|
||||
popNumber : Context -> Result (Context, I32) InterpreterErrors
|
||||
popNumber = \ctx ->
|
||||
when Context.popStack ctx is
|
||||
Ok (T popCtx (Number num)) -> Ok (T popCtx num)
|
||||
Ok (popCtx, Number num) -> Ok (popCtx, num)
|
||||
Ok _ -> Err (NoNumberOnStack)
|
||||
Err EmptyStack -> Err EmptyStack
|
||||
|
||||
popLambda : Context -> Result [T Context (List U8)] InterpreterErrors
|
||||
popLambda : Context -> Result (Context, List U8) InterpreterErrors
|
||||
popLambda = \ctx ->
|
||||
when Context.popStack ctx is
|
||||
Ok (T popCtx (Lambda bytes)) -> Ok (T popCtx bytes)
|
||||
Ok (popCtx, Lambda bytes) -> Ok (popCtx, bytes)
|
||||
Ok _ -> Err NoLambdaOnStack
|
||||
Err EmptyStack -> Err EmptyStack
|
||||
|
||||
popVariable : Context -> Result [T Context Variable] InterpreterErrors
|
||||
popVariable : Context -> Result (Context, Variable) InterpreterErrors
|
||||
popVariable = \ctx ->
|
||||
when Context.popStack ctx is
|
||||
Ok (T popCtx (Var var)) -> Ok (T popCtx var)
|
||||
Ok (popCtx, Var var) -> Ok (popCtx, var)
|
||||
Ok _ -> Err NoVariableOnStack
|
||||
Err EmptyStack -> Err EmptyStack
|
||||
|
|
|
@ -1,34 +1,30 @@
|
|||
module [line, withOpen, chunk, Handle]
|
||||
module [line!, withOpen!, chunk!, Handle]
|
||||
|
||||
import pf.PlatformTasks
|
||||
import pf.Host
|
||||
|
||||
Handle := U64
|
||||
|
||||
line : Handle -> Task Str *
|
||||
line = \@Handle handle ->
|
||||
PlatformTasks.getFileLine handle
|
||||
|> Task.mapErr \_ -> crash "unreachable File.line"
|
||||
line! : Handle => Str
|
||||
line! = \@Handle handle ->
|
||||
Host.getFileLine! handle
|
||||
|
||||
chunk : Handle -> Task (List U8) *
|
||||
chunk = \@Handle handle ->
|
||||
PlatformTasks.getFileBytes handle
|
||||
|> Task.mapErr \_ -> crash "unreachable File.chunk"
|
||||
chunk! : Handle => List U8
|
||||
chunk! = \@Handle handle ->
|
||||
Host.getFileBytes! handle
|
||||
|
||||
open : Str -> Task Handle *
|
||||
open = \path ->
|
||||
PlatformTasks.openFile path
|
||||
|> Task.mapErr \_ -> crash "unreachable File.open"
|
||||
|> Task.map @Handle
|
||||
open! : Str => Handle
|
||||
open! = \path ->
|
||||
Host.openFile! path
|
||||
|> @Handle
|
||||
|
||||
close : Handle -> Task.Task {} *
|
||||
close = \@Handle handle ->
|
||||
PlatformTasks.closeFile handle
|
||||
|> Task.mapErr \_ -> crash "unreachable File.close"
|
||||
close! : Handle => {}
|
||||
close! = \@Handle handle ->
|
||||
Host.closeFile! handle
|
||||
|
||||
withOpen : Str, (Handle -> Task {} a) -> Task {} a
|
||||
withOpen = \path, callback ->
|
||||
withOpen! : Str, (Handle => a) => a
|
||||
withOpen! = \path, callback! ->
|
||||
handle = open! path
|
||||
result = callback handle |> Task.result!
|
||||
result = callback! handle
|
||||
close! handle
|
||||
|
||||
Task.fromResult result
|
||||
result
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
hosted Host
|
||||
exposes [openFile!, closeFile!, getFileLine!, getFileBytes!, putLine!, putRaw!, getLine!, getChar!]
|
||||
imports []
|
||||
|
||||
openFile! : Str => U64
|
||||
|
||||
closeFile! : U64 => {}
|
||||
|
||||
getFileLine! : U64 => Str
|
||||
|
||||
getFileBytes! : U64 => List U8
|
||||
|
||||
putLine! : Str => {}
|
||||
|
||||
putRaw! : Str => {}
|
||||
|
||||
getLine! : {} => Str
|
||||
|
||||
getChar! : {} => U8
|
|
@ -1,21 +0,0 @@
|
|||
hosted PlatformTasks
|
||||
exposes [openFile, closeFile, withFileOpen, getFileLine, getFileBytes, putLine, putRaw, getLine, getChar]
|
||||
imports []
|
||||
|
||||
openFile : Str -> Task U64 {}
|
||||
|
||||
closeFile : U64 -> Task {} {}
|
||||
|
||||
withFileOpen : Str, (U64 -> Task ok err) -> Task {} {}
|
||||
|
||||
getFileLine : U64 -> Task Str {}
|
||||
|
||||
getFileBytes : U64 -> Task (List U8) {}
|
||||
|
||||
putLine : Str -> Task {} {}
|
||||
|
||||
putRaw : Str -> Task {} {}
|
||||
|
||||
getLine : Task Str {}
|
||||
|
||||
getChar : Task U8 {}
|
|
@ -1,16 +1,11 @@
|
|||
module [
|
||||
line,
|
||||
char,
|
||||
]
|
||||
module [line!, char!]
|
||||
|
||||
import pf.PlatformTasks
|
||||
import pf.Host
|
||||
|
||||
line : {} -> Task Str *
|
||||
line = \{} ->
|
||||
PlatformTasks.getLine
|
||||
|> Task.mapErr \_ -> crash "unreachable Stdin.line"
|
||||
line! : {} => Str
|
||||
line! = \{} ->
|
||||
Host.getLine! {}
|
||||
|
||||
char : {} -> Task U8 *
|
||||
char = \{} ->
|
||||
PlatformTasks.getChar
|
||||
|> Task.mapErr \_ -> crash "unreachable Stdin.char"
|
||||
char! : {} => U8
|
||||
char! = \{} ->
|
||||
Host.getChar! {}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
module [line, raw]
|
||||
module [line!, raw!]
|
||||
|
||||
import pf.PlatformTasks
|
||||
import pf.Host
|
||||
|
||||
line : Str -> Task {} *
|
||||
line = \text ->
|
||||
PlatformTasks.putLine text
|
||||
|> Task.mapErr \_ -> crash "unreachable Stdout.line"
|
||||
line! : Str => {}
|
||||
line! = \text ->
|
||||
Host.putLine! text
|
||||
|
||||
raw : Str -> Task {} *
|
||||
raw = \text ->
|
||||
PlatformTasks.putRaw text
|
||||
|> Task.mapErr \_ -> crash "unreachable Stdout.raw"
|
||||
raw! : Str => {}
|
||||
raw! = \text ->
|
||||
Host.putRaw! text
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
platform "false-interpreter"
|
||||
requires {} { main : Str -> Task {} [] }
|
||||
requires {} { main! : Str => {} }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
provides [mainForHost!]
|
||||
|
||||
mainForHost : Str -> Task {} []
|
||||
mainForHost = \file -> main file
|
||||
mainForHost! : Str => {}
|
||||
mainForHost! = \file -> main! file
|
||||
|
|
|
@ -20,20 +20,8 @@ fn file_handles() -> &'static Mutex<HashMap<u64, BufReader<File>>> {
|
|||
}
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(output: *mut u8, args: &RocStr);
|
||||
|
||||
#[link_name = "roc__mainForHost_1_exposed_size"]
|
||||
fn roc_main_size() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_0_caller"]
|
||||
fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8);
|
||||
|
||||
#[link_name = "roc__mainForHost_0_size"]
|
||||
fn size_Fx() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_0_result_size"]
|
||||
fn size_Fx_result() -> i64;
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn roc_main(args: &RocStr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -116,86 +104,51 @@ pub extern "C" fn rust_main() -> i32 {
|
|||
.expect("Please pass a .false file as a command-line argument to the false interpreter!");
|
||||
let arg = RocStr::from(arg.as_str());
|
||||
|
||||
let size = unsafe { roc_main_size() } as usize;
|
||||
|
||||
unsafe {
|
||||
let buffer = roc_alloc(size, 1) as *mut u8;
|
||||
|
||||
roc_main(buffer, &arg);
|
||||
|
||||
// arg has been passed to roc now, and it assumes ownership.
|
||||
// so we must not touch its refcount now
|
||||
std::mem::forget(arg);
|
||||
|
||||
let result = call_the_closure(buffer);
|
||||
|
||||
roc_dealloc(buffer as _, 1);
|
||||
|
||||
result
|
||||
};
|
||||
roc_main(&arg);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 {
|
||||
let size = size_Fx_result() as usize;
|
||||
let buffer = roc_alloc(size, 1) as *mut u8;
|
||||
|
||||
call_Fx(
|
||||
// This flags pointer will never get dereferenced
|
||||
MaybeUninit::uninit().as_ptr(),
|
||||
closure_data_ptr as *const u8,
|
||||
buffer as *mut u8,
|
||||
);
|
||||
|
||||
roc_dealloc(buffer as _, 1);
|
||||
0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getLine() -> RocResult<RocStr, ()> {
|
||||
pub extern "C" fn roc_fx_getLine() -> RocStr {
|
||||
let stdin = std::io::stdin();
|
||||
let line1 = stdin.lock().lines().next().unwrap().unwrap();
|
||||
|
||||
RocResult::ok(RocStr::from(line1.as_str()))
|
||||
RocStr::from(line1.as_str())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getChar() -> RocResult<u8, ()> {
|
||||
pub extern "C" fn roc_fx_getChar() -> u8 {
|
||||
let mut buffer = [0];
|
||||
|
||||
if let Err(ioerr) = std::io::stdin().lock().read_exact(&mut buffer[..]) {
|
||||
if ioerr.kind() == std::io::ErrorKind::UnexpectedEof {
|
||||
RocResult::ok(u8::MAX)
|
||||
u8::MAX
|
||||
} else {
|
||||
panic!("Got an unexpected error while reading char from stdin");
|
||||
}
|
||||
} else {
|
||||
RocResult::ok(buffer[0])
|
||||
buffer[0]
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_putLine(line: &RocStr) -> RocResult<(), ()> {
|
||||
pub extern "C" fn roc_fx_putLine(line: &RocStr) {
|
||||
let string = line.as_str();
|
||||
println!("{}", string);
|
||||
let _ = std::io::stdout().lock().flush();
|
||||
|
||||
RocResult::ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_putRaw(line: &RocStr) -> RocResult<(), ()> {
|
||||
pub extern "C" fn roc_fx_putRaw(line: &RocStr) {
|
||||
let string = line.as_str();
|
||||
print!("{}", string);
|
||||
let _ = std::io::stdout().lock().flush();
|
||||
|
||||
RocResult::ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getFileLine(br_id: u64) -> RocResult<RocStr, ()> {
|
||||
pub extern "C" fn roc_fx_getFileLine(br_id: u64) -> RocStr {
|
||||
let mut br_map = file_handles().lock().unwrap();
|
||||
let br = br_map.get_mut(&br_id).unwrap();
|
||||
let mut line1 = String::default();
|
||||
|
@ -203,11 +156,11 @@ pub extern "C" fn roc_fx_getFileLine(br_id: u64) -> RocResult<RocStr, ()> {
|
|||
br.read_line(&mut line1)
|
||||
.expect("Failed to read line from file");
|
||||
|
||||
RocResult::ok(RocStr::from(line1.as_str()))
|
||||
RocStr::from(line1.as_str())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_getFileBytes(br_id: u64) -> RocResult<RocList<u8>, ()> {
|
||||
pub extern "C" fn roc_fx_getFileBytes(br_id: u64) -> RocList<u8> {
|
||||
let mut br_map = file_handles().lock().unwrap();
|
||||
let br = br_map.get_mut(&br_id).unwrap();
|
||||
let mut buffer = [0; 0x10 /* This is intentionally small to ensure correct implementation */];
|
||||
|
@ -216,18 +169,16 @@ pub extern "C" fn roc_fx_getFileBytes(br_id: u64) -> RocResult<RocList<u8>, ()>
|
|||
.read(&mut buffer[..])
|
||||
.expect("Failed to read bytes from file");
|
||||
|
||||
RocResult::ok(RocList::from_slice(&buffer[..count]))
|
||||
RocList::from_slice(&buffer[..count])
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_closeFile(br_id: u64) -> RocResult<(), ()> {
|
||||
pub extern "C" fn roc_fx_closeFile(br_id: u64) {
|
||||
file_handles().lock().unwrap().remove(&br_id);
|
||||
|
||||
RocResult::ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_openFile(name: &RocStr) -> RocResult<u64, ()> {
|
||||
pub extern "C" fn roc_fx_openFile(name: &RocStr) -> u64 {
|
||||
let string = name.as_str();
|
||||
match File::open(string) {
|
||||
Ok(f) => {
|
||||
|
@ -236,7 +187,7 @@ pub extern "C" fn roc_fx_openFile(name: &RocStr) -> RocResult<u64, ()> {
|
|||
|
||||
file_handles().lock().unwrap().insert(br_id, br);
|
||||
|
||||
RocResult::ok(br_id)
|
||||
br_id
|
||||
}
|
||||
Err(_) => {
|
||||
panic!(
|
||||
|
@ -246,17 +197,3 @@ pub extern "C" fn roc_fx_openFile(name: &RocStr) -> RocResult<u64, ()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn roc_fx_withFileOpen(_name: &RocStr, _buffer: *const u8) -> RocResult<(), ()> {
|
||||
// TODO: figure out accepting a closure in an fx and passing data to it.
|
||||
// let f = File::open(name.as_str()).expect("Unable to open file");
|
||||
// let mut br = BufReader::new(f);
|
||||
|
||||
// unsafe {
|
||||
// let closure_data_ptr = buffer.offset(8);
|
||||
// call_the_closure(closure_data_ptr);
|
||||
// }
|
||||
|
||||
RocResult::ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue