2
\$\begingroup\$

I am trying to implement a small programming language, in Nim, as a way of improving my (close to non-existent) programming skills. The programming language is intended to have IF-GOTO, variables, and the ability to read from stdin. First, I wrote the VM, this thing:

import strutils
#Nim module for string handling
proc parseFile(filename: string): seq[string] =
  var rawcode = readFile(filename)
  var code = rawcode.split("\n")
  return code
#Parses file into seq of 1-line strings


proc main(mem: var seq[int]) =
  var code = parseFile(readLine(stdin))
  #Gets filename from stdin, reads it, parses it, and assigns it to code.
  var codeptr = 0;
  #Points to line of code currently being processed
  while codeptr < len(code):

    
    var currins = code[codeptr].split(" ")
    #current line of code being processed
    if currins[0] == "store":
      var IP2213 = currins[1].parseInt()
      mem[IP2213] = currins[2].parseInt()
    #Store stuff in mem.
    if currins[0] == "addstore":
      var IP = currins[1].parseInt()
      mem[IP] = mem[IP] + currins[2].parseInt()
    #mem[address] += a new number

    elif currins[0] == "substore":
      var IP1 = currins[1].parseInt()
      mem[IP1] = mem[IP1] - currins[2].parseInt()
    #mem[address] -= a new number
    elif currins[0] == "add":
      var IP2 = currins[1].parseInt()
      var IP3 = currins[2].parseInt()
      var IP4 = currins[3].parseInt()
      mem[IP4] = mem[IP2] + mem[IP3]
    #adds two numbers in traditional register machine way.

    elif currins[0] == "sub":
      var IP5 = currins[1].parseInt()
      var IP6 = currins[2].parseInt()
      var IP7 = currins[3].parseInt()
      mem[IP7] = mem[IP5] - mem[IP6]
      #subs two numbers in traditional register machine way.

    elif currins[0] == "mul":
      var IP8 = currins[1].parseInt()
      var IP9 = currins[2].parseInt()
      var IP10 = currins[3].parseInt()
      mem[IP10] = mem[IP8] * mem[IP9]
      #muls two numbers in traditional register machine way.

    elif currins[0] == "div":
      var IP11 = currins[1].parseInt()
      var IP12 = currins[2].parseInt()
      var IP13 = currins[3].parseInt()
      mem[IP13] = int(mem[IP11] / mem[IP12])
      #divs two numbers in traditional register machine way.

    elif currins[0] == "swap":
      var IP20 = currins[1].parseInt()
      var IP21 = currins[2].parseInt()
      var temp = mem[IP21]
      mem[IP21] = mem[IP20]
      mem[IP20] = temp
    #swaps the values of two mem locations
    elif currins[0] == "cp":
      var IP22 = currins[1].parseInt()
      var IP23 = currins[2].parseInt()
      mem[IP23] = mem[IP22] 
    #copies the value of one location unto another
    elif currins[0] == "inp":
      var IP223 = currins[1].parseInt()
      mem[IP223] = readLine(stdin).parseInt()

    #take one int of input and place it mem.
    elif currins[0] == "finp":
      var IP223 = currins[1].parseInt()
      mem[IP223] = ord(readLine(stdin)[0])
    #take one char of input and place it mem.
    elif currins[0] == "jmpifnot":
      var IP30 = currins[1].parseInt()
      var IP31 = currins[2]
      var IP32 = currins[3].parseInt()
      var IP33 = currins[4]
      var IP34 = currins[5].parseInt()
      if IP31 == "=":
        if mem[IP30] != mem[IP32]:
          if IP33 == "-":
            codeptr -= IP34
          elif IP33 == "+":
            codeptr += IP34
      elif IP31 == "!":
        if mem[IP30] == mem[IP32]:
          if IP33 == "-":
            codeptr -= IP34
          elif IP33 == "+":
            codeptr += IP34
    #if statement. Jumps forward or backward if == or !=
    elif currins[0] == "fecho":
      var IP217 = currins[1].parseInt()
      echo chr(mem[IP217])
    #echos char at mem
    elif currins[0] == "echo":
      var IP37 = currins[1].parseInt()
      echo mem[IP37]
    #echos int at mem
    inc(codeptr)
    



var mem = newSeq[int](10000);
#makes mem array
main(mem)
#runs it

Then I wrote another script that uses a dictionary to have variables, and be slightly more readable.

import strutils, tables, strformat
#Imports Nim modules needed.

proc parseFile(filename: string): seq[string] =
  var rawcode = readFile(filename)
  var code = rawcode.split("\n")
  return code
#Splits file into seq of lines

var f = open("c.txt", fmWrite)
#file to write the code for the VM to. 

var vartable = initTable[string, int]()
#Table to hold the variables and the mem locations they represent.

var code = parseFile(readLine(stdin))
#Gets the seq of lines needed.

var codeptr = 0
#Code line pointer

var avail = 0
#Available mem locations to assign variables to.

var truereal: string

proc main() =


  while codeptr < len(code):
    #Main loop    

    var cur: seq[string]
    #Current line to process

    cur = code[codeptr].split(" ")
    for i in 0..20:
      cur.add(" ")
    if cur[0] == "var":
      vartable[cur[1]] = avail
      avail = avail + 1
    #For var declaration

    elif cur[1] == "=":
      if not haskey(vartable, cur[0]):
        vartable[cur[0]] = avail
        avail = avail + 1
      f.write(fmt"store {vartable[cur[0]]} {cur[2].parseInt()}", "\n")
    #Assigns value to var

    elif cur[2] == "+":
      if not haskey(vartable, cur[0]):
        vartable[cur[0]] = avail
        avail = avail + 1
      f.write(fmt"add {vartable[cur[1]]} {vartable[cur[3]]} {vartable[cur[0]]}", "\n")
    #Adds to variables together and puts the result into a third one

    elif cur[2] == "-":
      if not haskey(vartable, cur[0]):
        vartable[cur[0]] = avail
        avail = avail + 1
      f.write(fmt"sub {vartable[cur[1]]} {vartable[cur[3]]} {vartable[cur[0]]}", "\n")
    #Subtracts two variables from each other and puts the result into a third one

    elif cur[2] == "*":
      if not haskey(vartable, cur[0]):
        vartable[cur[0]] = avail
        avail = avail + 1
      f.write(fmt"mul {vartable[cur[1]]} {vartable[cur[3]]} {vartable[cur[0]]}", "\n")
    #Multiplies two variables by each other and puts the result into a third one

    elif cur[2] == "/":
      if not haskey(vartable, cur[0]):
        vartable[cur[0]] = avail
        avail = avail + 1
      f.write(fmt"div {vartable[cur[1]]} {vartable[cur[3]]} {vartable[cur[0]]}", "\n")
    #Divides two variables by each other and puts the result into a third one

    elif cur[0] == "echonum":
      f.write(fmt"echo {vartable[cur[1]]}", "\n")
    #Echos the int value of a var

    elif cur[0] == "echochar":
      f.write(fmt"fecho {vartable[cur[1]]}", "\n")
    #Echos the char value of a var

    elif cur[0] == "input":
      f.write(fmt"inp {vartable[cur[1]]}", "\n")
    #Takes one int of input
    elif cur[0] == "inputchar":
      f.write(fmt"finp {vartable[cur[1]]}", "\n")
    #takes one char of input
    elif cur[0] == "if":
      var list: string
      var curl = codeptr
      var jto = cur[4].parseInt() + curl
      var li: int
      while curl < jto:
        list = " " & list & code[curl] & " "

        curl = curl + 1
      for i in list.split(" "):
        if i == "if":
          li = li + 1
        elif i == "+":
          li = li + 1
        elif i == "-":
          li = li + 1
        elif i == "*":
          li = li + 1
        elif i == "/":
          li = li + 1
        elif i == "goto":
          li = li + 1
        elif i == "echonum":
          li = li + 1
        elif i == "=":
          li = li + 1
        elif i == "echochar":
          li = li + 1
        elif i == "input":
          li = li + 1
        elif i == "inputchar":
          li = li + 1

      f.write(fmt"jmpifnot {vartable[cur[1]]} {cur[2]} {vartable[cur[3]]} + {li}", "\n")
      

    #Jumps some lines forward if the condition is not true. An if statement of sorts.

    elif cur[0] == "goto":

      var list: string
      var curl = codeptr
      var jto = curl - cur[4].parseInt()
      var li: int
      while curl > jto:
        list = " " & list & code[jto] & " "
        jto = jto + 1
      for i in list.split(" "):
        if i == "if":
          li = li + 1
        elif i == "+":
          li = li + 1
        elif i == "-":
          li = li + 1
        elif i == "*":
          li = li + 1
        elif i == "/":
          li = li + 1
        elif i == "goto":
          li = li + 1
        elif i == "echonum":
          li = li + 1
        elif i == "=":
          li = li + 1
        elif i == "echochar":
          li = li + 1
        elif i == "input":
          li = li + 1
        elif i == "inputchar":
          li = li + 1
      if cur[2] == "=":
        truereal = "!"
      elif cur[2] == "!":
        truereal = "="   
      f.write(fmt"jmpifnot {vartable[cur[1]]} {truereal} {vartable[cur[3]]} - {li}", "\n")
    #Jumps some lines backward if the condition is true. An goto statement of sorts.

      

    codeptr = codeptr + 1

main()

Small example program:

var B
var C
var Q
input B
input C
input Q
echonum B
echonum C
echonum Q

Which should print the 3 numbers you enter.

\$\endgroup\$
2
  • \$\begingroup\$ Is this the right version of the code? I'm just getting Error: undeclared identifier: 'branched' when trying to run the first snippet. \$\endgroup\$
    – ferada
    Jun 18 at 9:35
  • \$\begingroup\$ Sorry! I was trying something stupid and left the code in. \$\endgroup\$ Jun 19 at 6:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.