roc/crates/cli/tests/test-projects/false-interpreter/Context.roc
Jan 68e524e110
snake_case-ify all remaining camelCase names in hiding (#7561)
* Snake-case-ify def names in comments

* Snake-case-ify def names in strings

* Snake-case-ify ignored function parameters

* Snake-case-ify test script names, for consistency

* Update CI snapshot to match snake_case

* snake case correction

---------

Co-authored-by: Anton-4 <17049058+Anton-4@users.noreply.github.com>
2025-02-05 16:05:03 +01:00

108 lines
4.1 KiB
Text

module [Context, Data, with!, get_char!, Option, push_stack, pop_stack, to_str, in_while_scope]
import pf.File
import Variable exposing [Variable]
Option a : [Some a, None]
# The underlying context of the current location within the file
Data : [Lambda (List U8), Number I32, Var Variable]
# While loops are special and have their own Scope specific state.
WhileState : { cond : List U8, body : List U8, state : [InCond, InBody] }
Scope : { data : Option File.Handle, index : U64, buf : List U8, while_info : Option WhileState }
State : [Executing, InComment, InLambda U64 (List U8), InString (List U8), InNumber I32, InSpecialChar, LoadChar]
Context : { scopes : List Scope, stack : List Data, vars : List Data, state : State }
push_stack : Context, Data -> Context
push_stack = \ctx, data ->
{ ctx & stack: List.append(ctx.stack, 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
pop_stack : Context -> Result (Context, Data) [EmptyStack]
pop_stack = \ctx ->
when List.last(ctx.stack) is
Ok(val) ->
popped_ctx = { ctx & stack: List.drop_at(ctx.stack, (List.len(ctx.stack) - 1)) }
Ok((popped_ctx, val))
Err(ListWasEmpty) ->
Err(EmptyStack)
to_str_data : Data -> Str
to_str_data = \data ->
when data is
Lambda(_) -> "[]"
Number(n) -> Num.to_str(Num.int_cast(n))
Var(v) -> Variable.to_str(v)
to_str_state : State -> Str
to_str_state = \state ->
when state is
Executing -> "Executing"
InComment -> "InComment"
InString(_) -> "InString"
InNumber(_) -> "InNumber"
InLambda(_, _) -> "InLambda"
InSpecialChar -> "InSpecialChar"
LoadChar -> "LoadChar"
to_str : Context -> Str
to_str = \{ scopes, stack, state, vars } ->
depth = Num.to_str(List.len(scopes))
state_str = to_str_state(state)
stack_str = Str.join_with(List.map(stack, to_str_data), " ")
vars_str = Str.join_with(List.map(vars, to_str_data), " ")
"\n============\nDepth: ${depth}\nState: ${state_str}\nStack: [${stack_str}]\nVars: [${vars_str}]\n============\n"
with! : Str, (Context => a) => a
with! = \path, callback! ->
File.with_open!(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: [], while_info: None }
callback!({ scopes: [{ data: Some(handle), index: 0, buf: [], while_info: None }], state: Executing, stack: [], vars: List.repeat(Number(0), Variable.total_count) }))
# I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is.
get_char! : Context => Result (U8, Context) [EndOfData, NoScope]
get_char! = \ctx ->
when List.last(ctx.scopes) is
Ok(scope) ->
(val, new_scope) = get_char_scope!?(scope)
Ok((val, { ctx & scopes: List.set(ctx.scopes, (List.len(ctx.scopes) - 1), new_scope) }))
Err(ListWasEmpty) ->
Err(NoScope)
get_char_scope! : Scope => Result (U8, Scope) [EndOfData, NoScope]
get_char_scope! = \scope ->
when List.get(scope.buf, scope.index) is
Ok(val) ->
Ok((val, { scope & index: scope.index + 1 }))
Err(OutOfBounds) ->
when scope.data is
Some(h) ->
bytes = File.chunk!(h)
when List.first(bytes) is
Ok(val) ->
# This starts at 1 because the first character is already being returned.
Ok((val, { scope & buf: bytes, index: 1 }))
Err(ListWasEmpty) ->
Err(EndOfData)
None ->
Err(EndOfData)
in_while_scope : Context -> Bool
in_while_scope = \ctx ->
when List.last(ctx.scopes) is
Ok(scope) ->
scope.while_info != None
Err(ListWasEmpty) ->
Bool.false