<p>I am currently writing a Funge-98 interpreter in Go following the <a href="https://github.com/catseye/Funge-98/blob/master/doc/funge98.markdown" rel="noreferrer">Funge-98 specification</a> and need to handle all of its built-in instructions. However, this is proving to be unwieldy, because there's several many built-in instructions that each do their own thing.</p>
<p>Currently, I have a map of an instruction's character to a function pointer that will actually perform the instruction:</p>
<pre class="lang-go prettyprint-override"><code>interpreter.instructions = map[rune]func() {
    '^': PointNorth,
    '&gt;': PointEast,
    'v': PointSouth,
    '&lt;': PointWest,
// ...
    '0': PushNumber, // PushNumber checks the current instruction for what to push
    '1': PushNumber,
    '2': PushNumber,
// ...
}
</code></pre>
<p>and indexing into this map based on the current cell:</p>
<pre class="lang-go prettyprint-override"><code>if interpreter.CurrentInstruction() == ' ' { /* nop */ }
else if instr, ok := interpreter.instructions[r]; ok { instr() }
else { /* ... */ }
</code></pre>
<p>Already, I have 40 instructions implemented and this map is getting long and unwieldy.</p>
<p><strong>What other ways of organizing this many built-ins are there?</strong></p>

<implementation><interpreters><built-ins>
Organizing a large number of built-in instructions
<p>Fundamentally, all of your builtins have to be in one place at some point. If your concern is source code bloat, you can try to modularize the initialization function into several smaller functions. That looks like Go, which I'm not intimately familiar with, so I'll write a bit of Rust pseudocode here. Instead of</p>
<pre class="lang-rust prettyprint-override"><code>pub fn initialize_builtins() -&gt; HashMap&lt;char, Function&gt; {
  let mut hashmap = HashMap::new();
  hashmap.insert('^', ...);
  hashmap.insert('&lt;', ...);
  hashmap.insert('&gt;', ...);
  hashmap.insert('v', ...);
  hashmap.insert('0', ...);
  hashmap.insert('1', ...);
  hashmap.insert('2', ...);
  hashmap.insert('3', ...);
  hashmap
}
</code></pre>
<p>you can split that into several different functions (maybe even different files) like so</p>
<pre class="lang-rust prettyprint-override"><code>pub fn initialize_builtins() -&gt; HashMap&lt;char, Function&gt; {
  let mut hashmap = HashMap::new();
  init_directions(&amp;mut hashmap);
  init_numbers(&amp;mut hashmap);
  hashmap
}

priv fn init_directions(hashmap: &amp;mut HashMap&lt;char, Function&gt;) {
  hashmap.insert('^', ...);
  hashmap.insert('&lt;', ...);
  hashmap.insert('&gt;', ...);
  hashmap.insert('v', ...);
}

priv fn init_numbers(hashmap: &amp;mut HashMap&lt;char, Function&gt;) {
  hashmap.insert('0', ...);
  hashmap.insert('1', ...);
  hashmap.insert('2', ...);
  hashmap.insert('3', ...);
}
</code></pre>
<p>Those private helper functions can be relegated to their own modules if you like.</p>

CC BY-SA 4.0
2023-05-24T14:26:07.730Z
2023-05-24T14:26:07.730Z
Silvio Mayolo
<p>If the implementation language supports it, you may wish to use <a href="https://en.wikipedia.org/wiki/Decorator_pattern" rel="nofollow noreferrer">Decorators</a>.</p>
<p>Decorators allow you to keep registration of built-ins attached to their definition, which can allow for cleaner, more extendable code.</p>
<p>This does however mean there is no clear list of Built-ins in the source, which can make it harder for other programmers to find them. It also introduces some complications when debugging, as Decorators don't carry the same guarantees regarding execution timing.</p>
<p>An example of this implementation could be:</p>
<pre class="lang-typescript prettyprint-override"><code>function builtin(...symbols: string[]) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    symbols.forEach(s=&gt;interpreter.instructions[s] = target);
  };
}

@builtin(&quot;^&quot;)
function PointNorth(symbol: String){
}

@builtin(&quot;0&quot;,&quot;1&quot;,&quot;2&quot;,&quot;3&quot;,&quot;4&quot;,&quot;5&quot;,&quot;6&quot;,&quot;7&quot;,&quot;8&quot;,&quot;9&quot;)
function PushNumber(symbol: String){
}
</code></pre>

CC BY-SA 4.0
2023-05-26T04:41:03.360Z
2023-05-26T04:41:03.360Z
ATaco
# Organizing a large number of built-in instructions
Posted by: username_0 on 2023-05-24
I am currently writing a Funge-98 interpreter in Go following the Funge-98 specification and need to handle all of its built-in instructions. However, this is proving to be unwieldy, because there's several many built-in instructions that each do their own thing.
Currently, I have a map of an instruction's character to a function pointer that will actually perform the instruction:
interpreter.instructions = map[rune]func() {
    '^': PointNorth,
    '>': PointEast,
    'v': PointSouth,
    '<': PointWest,
// ...
    '0': PushNumber, // PushNumber checks the current instruction for what to push
    '1': PushNumber,
    '2': PushNumber,
// ...
}

and indexing into this map based on the current cell:
if interpreter.CurrentInstruction() == ' ' { /* nop */ }
else if instr, ok := interpreter.instructions[r]; ok { instr() }
else { /* ... */ }

Already, I have 40 instructions implemented and this map is getting long and unwieldy.
What other ways of organizing this many built-ins are there?
**Tags:** implementation interpreters built-ins
---
## 2 answers
### Answer by username_1 on 2023-05-24. Score: 6
Fundamentally, all of your builtins have to be in one place at some point. If your concern is source code bloat, you can try to modularize the initialization function into several smaller functions. That looks like Go, which I'm not intimately familiar with, so I'll write a bit of Rust pseudocode here. Instead of
pub fn initialize_builtins() -> HashMap {
  let mut hashmap = HashMap::new();
  hashmap.insert('^', ...);
  hashmap.insert('<', ...);
  hashmap.insert('>', ...);
  hashmap.insert('v', ...);
  hashmap.insert('0', ...);
  hashmap.insert('1', ...);
  hashmap.insert('2', ...);
  hashmap.insert('3', ...);
  hashmap
}

you can split that into several different functions (maybe even different files) like so
pub fn initialize_builtins() -> HashMap {
  let mut hashmap = HashMap::new();
  init_directions(&mut hashmap);
  init_numbers(&mut hashmap);
  hashmap
}

priv fn init_directions(hashmap: &mut HashMap) {
  hashmap.insert('^', ...);
  hashmap.insert('<', ...);
  hashmap.insert('>', ...);
  hashmap.insert('v', ...);
}

priv fn init_numbers(hashmap: &mut HashMap) {
  hashmap.insert('0', ...);
  hashmap.insert('1', ...);
  hashmap.insert('2', ...);
  hashmap.insert('3', ...);
}

Those private helper functions can be relegated to their own modules if you like.
---
### Answer by username_2 on 2023-05-26. Score: 4
If the implementation language supports it, you may wish to use Decorators.
Decorators allow you to keep registration of built-ins attached to their definition, which can allow for cleaner, more extendable code.
This does however mean there is no clear list of Built-ins in the source, which can make it harder for other programmers to find them. It also introduces some complications when debugging, as Decorators don't carry the same guarantees regarding execution timing.
An example of this implementation could be:
function builtin(...symbols: string[]) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    symbols.forEach(s=>interpreter.instructions[s] = target);
  };
}

@builtin("^")
function PointNorth(symbol: String){
}

@builtin("0","1","2","3","4","5","6","7","8","9")
function PushNumber(symbol: String){
}
---
