Я пытаюсь реализовать небольшой язык программирования в Nim, чтобы улучшить свои (почти несуществующие) навыки программирования. Язык программирования предназначен для использования IF-GOTO, переменных и возможности чтения из стандартного ввода. Сначала я написал ВМ, вот такую штуку:
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
branched = true
elif IP31 == "!":
if mem[IP30] == mem[IP32]:
if IP33 == "-":
codeptr -= IP34
elif IP33 == "+":
codeptr += IP34
branched = true
#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
if not branched:
inc(codeptr)
var mem = newSeq[int](10000);
#makes mem array
main(mem)
#runs it
Затем я написал другой сценарий, который использует словарь для переменных и делает его немного более читаемым.
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()
Небольшой пример программы:
var B
var C
var Q
input B
input C
input Q
echonum B
echonum C
echonum Q
При этом должны быть напечатаны 3 числа, которые вы вводите.
