aboutsummaryrefslogtreecommitdiff
path: root/vm.bqn
diff options
context:
space:
mode:
Diffstat (limited to 'vm.bqn')
-rw-r--r--vm.bqn39
1 files changed, 28 insertions, 11 deletions
diff --git a/vm.bqn b/vm.bqn
index d2e34eb0..0293d039 100644
--- a/vm.bqn
+++ b/vm.bqn
@@ -12,7 +12,7 @@ MakeVar ← { program 𝕊 name:
Get ⇐ !∘"Runtime: Variable referenced before definition"
SetU ⇐ !∘"↩: Variable modified before definition"
SetN ⇐ {
- Get ↩ {𝕤⋄v}
+ Get ↩ {𝕊:v}
(SetU ↩ {v↩𝕩}) 𝕩
}
SetQ ⇐ 0∘SetN
@@ -33,9 +33,12 @@ ref ← {
# Array destructuring ⟨a,b,c⟩←
Array ⇐ {𝕊 arr:
Get ⇐ {𝕩.Get@}¨ arr˙
+ # Common code for all setter functions
+ # 𝕨S𝕩 sets reference 𝕨 to 𝕩, and e indicates error handling
_set_ ← {S _𝕣_ e:
- Err ← {(e∾": "∾𝕩)!e≡@ ⋄ ⟨1⟩}
- c ← (e≡@) ⊑ {𝔽}‿{𝔽⎊1} # GetF or Get in F can error
+ Err ← {(e∾": "∾𝕩)!e≡@ ⋄ ⟨1⟩} # e≡@ indicates SetQ, which can't error
+ c ← (e≡@) ⊑ {𝔽}‿{𝔽⎊1} # GetF or Get in F can error
+ # Get field for reference 𝕨 from namespace, if possible
F ← {⟨G⇐GetF⟩𝕊𝕩:(G𝕩).Get@ ; !Err"Cannot extract non-name from namespace"}
{
0=•Type𝕩 ? arr ≡○≢◶⟨Err∘"Target and value shapes don't match", S¨⟩ 𝕩 ;
@@ -47,6 +50,8 @@ ref ← {
SetU ⇐ {𝕨.SetU𝕩}_set_"↩"
SetQ ⇐ ∨´ {𝕨.SetQ𝕩}_set_@
}
+ # Alias, like ⇐vals in ⟨a‿b⇐vals⟩←
+ # It behaves like ⟨a‿b⟩← except when destructuring a namespace (GetF)
Alias ⇐ {env‿name 𝕊 r:
SetN‿SetU‿SetQ ⇐ r
GetF ⇐ {env.program 𝕩.Field name}
@@ -83,7 +88,8 @@ MakeEnv ← { 𝕊⟨
# The input is taken from the bytecode stream.
VO ← { d←𝕏@, s←𝕏@, s⊑·{𝕩.vars}{𝕩.parent}⍟d }
-Namespace ← {𝕩.MakeNS@} # Namespace from environment
+# Namespace from environment
+Namespace ← {𝕩.MakeNS@}
# Read field 𝕨 from program 𝕩, where 𝕨 is the environment and index
GetField ← { e‿i 𝕊 𝕩:
"Key lookup in non-namespace" ! 6=•Type𝕩
@@ -92,19 +98,24 @@ GetField ← { e‿i 𝕊 𝕩:
# Constants
nothing ← {⇐} # Used when 𝕨 is ·
-skipMark ← {⇐}
+skipMark ← {⇐} # Indicates body aborted instead of returning
+# Execution stack: every body evaluation makes one of these
MakeStack ← {
s ← 𝕩 # Stack (a list)
cont ⇐ 1 # Whether to continue execution
rslt ⇐ skipMark # Result: skipMark to abort current body
Push ⇐ {s∾↩<𝕩} # Push a value
Pop ⇐ {t←-𝕩 ⋄ (s↓˜↩t) ⊢ ⌽t↑s} # Pop 𝕩 values; return as list
- Peek ⇐ {𝕤⋄¯1⊑s} # Return but don't pop top value
+ Peek ⇐ {𝕊:¯1⊑s} # Return but don't pop top value
Ret ⇐ {rslt↩𝕩 ⋄ cont↩0 ⋄ "Internal compiler error: Wrong stack size"!𝕨≥≠s}
- Skip ⇐ {𝕤⋄ cont↩0}
+ Skip ⇐ {𝕊: cont↩0} # Exit with no return value
}
+# All the opcodes
+# Each one is a function that takes the next-opcode function so it can
+# read values from the bytecode stream. It returns a function to be
+# called on the stack s and environment e at evaluation time.
ops ← ((!∘"Unknown opcode")˙⊣´⊢)¨ ⊔˝ ⍉> ⟨
# Constants and drop
0‿{i←𝕏@ ⋄ {s𝕊e: s.Push i⊑e.program.consts } }
@@ -147,36 +158,41 @@ ops ← ((!∘"Unknown opcode")˙⊣´⊢)¨ ⊔˝ ⍉> ⟨
66‿{i←𝕏@ ⋄ {s𝕊e: s.Push e‿i ref.Alias ⊑s.Pop 1 } }
-RunBC ← { bc‿pos‿env:
- Next ← {𝕤⋄ (pos+↩1) ⊢ pos⊑bc }
+# Evaluate a body
+RunBC ← { bc‿pos‿env: # bytecode, starting position, environment
+ Next ← {𝕊: (pos+↩1) ⊢ pos⊑bc }
stack ← MakeStack ⟨⟩
Step ← {𝕊:
op ← (Next@) ⊑ ops
op ↩ Op next
stack Op env
- stack.cont
+ stack.cont # Changes to 0 on return or abort
}
_while_ ← {𝔽⍟𝔾∘𝔽_𝕣_𝔾∘𝔽⍟𝔾𝕩}
Step _while_ ⊢ 1
stack.rslt
}
+# Evaluate a program, given the compiler output
{ VM bc‿consts‿blockInfo‿bodyInfo‿loc‿token:
bodies ← {start‿vars‿names‿export:
+ # Called when the body is evaluated
{parent 𝕊 args:
env ← MakeEnv parent‿vars‿names‿export
- (⊢ {𝕩.SetN 𝕨}¨ ≠↑env.vars˙) args
+ (⊢ {𝕩.SetN 𝕨}¨ ≠↑env.vars˙) args # Initialize arguments
RunBC bc‿start‿env
}
}¨ bodyInfo
blocks ← {type‿imm‿body:
+ # Handle operands
inner ← imm ⊑ type ⊑ ⟨
2⥊⟨{𝕊n: N ⟨⟩}⟩
{𝕊n: {d←N 𝕣‿𝕗 ⋄𝕨D𝕩}}‿{𝕊n: {N 𝕣‿𝕗 }}
{𝕊n: {d←N 𝕣‿𝕗‿𝕘⋄𝕨D𝕩}}‿{𝕊n: {N 𝕣‿𝕗‿𝕘}}
+ # Handle arguments
outer ← imm ⊑ ⟨
{
m‿d: {𝕊v: {M 𝕤‿𝕩‿nothing∾v;D 𝕤‿𝕩‿𝕨∾v}} ;
@@ -185,6 +201,7 @@ RunBC ← { bc‿pos‿env:
+ # Assemble bodies
nmc ← "No matching case"
Then ← {first 𝕊 next: {skipMark≢r←𝕨First𝕩 ? r ; 𝕨Next𝕩}}
run ← {