It took me one afternoon to complete a simple language interpreter. I will post all the code at the end, but today I am not going to explain every step of the program in detail, it may take some time to look for internships and papers recently. Once I have time, I will explain each part. Here I will only remind readers that the writing process of a program is different from what it shows, in general, my writing process is to first write an interpreter that contains only one instruction, and then gradually add other instructions.Ps:How much I want to see the process of writing a program by masters, not just the result, but as Graham said, "the process of writing is often lengthy," So there are not many books in this regard. I think similar books include clean code, paip, and software.
Tools. It is a pity that only one and a half of the books have been read.
Idea Source
Yesterday, I was bored when I sat in a lab that couldn't access the internet. Then I remembered that when I learned "Computability and computational complexity", I used an original language containing five instructions. The teacher also asked us to use this language to write programs, so I want to write an interpreter to execute this language and use this interpreter as my own Christmas gift.
Original language description
The book says that it is a simple language in FORTRAN, so I gave it a simple name, metafor, indicating meta FORTRAN. Next, let's take a look at its specific description, and paste it with my more detailed description in the code, I name each command (note that the value assignment statement is named SETF because I recently wrote more Common Lisp statements ).
+ Shards +
Instruction description
X = x + 1 variable X value plus 1
The value of X = x-1 is reduced by 1. If the value of X is 0, the result is still 0.
To a if x = 0 if x = 0, convert the command labeled as a; otherwise, execute the next command.
To a is unconditionally transferred TO the command labeled
Y = x assigns the value of x to the variable y, and the value of x remains unchanged.
+ Shards +
1. metafor has five instructions: name instruction descriptioninc: x = x + 1, Increase by 1 the value of the variable x. dec: x = x-1, If the value of x is 0, leave it unchanged; otherwise decrease by 1 the value of x. con_goto: to a if x! = 0, If the value of x is nonzero, perform the instruction with the label A next; otherwise proceed to the next instruction in the list. goto: to a, Perform the instruction with the label A next. setf: y = x, Change the value variable y to the value of variable x.2. Some specification :( 1) Input variables are represented as: x1, x2, x3 ,... (2) Local variables are represented as: z1, z2, z3 ,... (3) Outpu T variable is represented as: ynote: the num 1 is often omitted (I. e ., x stands for x1 and z standsfor z1 ). 3. labeled instructions: Instructions may or may not have labels. when an instruction is labeled, the label is written to its left in square brackets. for example, [B] Z = Z-l4. A smaple program: "Program to compute y = x1 + x2" y = x1 [B] TO A IF x2! = 0 to e [A] x2 = x2-1 y = y + 1 TO B4. For more information, please refer to Computability and computing complexity, by Zhou Changlin and Li zhanshan.
Overall Thinking
Because the language contains a goto statement, I cannot explain the execution of a sentence. I need to read the entire program and convert it into an internal format for execution as a whole. For example, this is a language program that calculates y = x + x2:
y = x[B] TO A IF x2 != 0 TO E[A] x2 = x2 - 1 y = y + 1 TO B
After it is converted to an internal format, it is:
[['setf', 'y', 'x'], ['labeled_exp', 'B', ['con_goto', 'A', 'x2']], ['goto', 'E'], ['labeled_exp', 'A', ['dec', 'x2']], ['inc', 'y'], ['goto', 'B']]
Run the demo -- get the program from the file
Or the preceding y = x + x2 program. First, I need to assign values to two input parameters (x, x2). If they are not assigned a value, their values are 0 by default. In addition, we can also view the internal representation structure of the program and perform simple debugging (view the values of other environment variables ). The demo result is as follows:
======================================================*Metafor 1.0, Welecome to Metafor shell environment. **Author: Zhu zhaolong(zzljlu@gmail.com) *======================================================Metafor> 2 35Metafor> 5 611Metafor> code[['setf', 'y', 'x'], ['labeled_exp', 'B', ['con_goto', 'A', 'x2']], ['goto', 'E'], ['labeled_exp', 'A', ['dec', 'x2']], ['inc', 'y'], ['goto', 'B']]>>> ========================================== RESTART ==========================================>>> ======================================================*Metafor 1.0, Welecome to Metafor shell environment. **Author: Zhu zhaolong(zzljlu@gmail.com) *======================================================Metafor> 234 5239Metafor> debugdebug> x234debug> x20debug> y239debug> exit>>>
Run the demo-enter the original language program directly from the End User
I wrote a simple repl interaction environment, so that we can easily test simple original language programs. For example, the following shows a program with y = x + 1, for example:
>>> repl()======================================================*Metafor 1.0, Welecome to Metafor shell environment. **Author: Zhu zhaolong(zzljlu@gmail.com) *======================================================Input your program:x = x + 1y = xinputs> 5=>6Input your program:
All code
Here I post all the python code:
"Metafor: Meta Fortran, is a small language used in" computabilit and Complexity "course in Jilin University. @ Author: Zhu zhaolong (zzljlu@gmail.com) @ Date: 2011.12.133. metafor has five instructions: Name instruction descriptioninc: x = x + 1, increase by 1 the value of the variable X. dec: x = x-1, if the value of X is 0, leave it unchanged; otherwise decrease by 1 the value of X. con_goto: To a I F x! = 0, if the value of X is nonzero, perform the instruction with the label a next; otherwise proceed to the next instruction in the list. goto: To A, perform the instruction with the label a next. SETF: Y = x, change the value variable Y to the value of variable x.2. some specification :( 1) input variables are represented as: x1, x2, X3 ,... (2) local variables are represented as: Z1, Z2, Z3 ,... (3) outpu T variable is represented as: ynote: the NUM 1 is often omitted (I. E ., X stands for X1 and Z standsfor Z1 ). 3. labeled instructions: Instructions may or may not have labels. when an instruction is labeled, the label is written to its left in square brackets. for example, [B] Z = z-l4. A smaple program: "program to compute y = X1 + x2" Y = x1 [B] to a if X2! = 0 to E [a] X2 = x2-1 y = Y + 1 to B4. For more information, please refer to Computability and computing complexity, by Zhou Changlin and Li zhanshan. "###======================================== ================================== import refrom pprint import pprint ###============== ======================= parse ================================== = def parse (Program): return [Read (e) For E in program] def read (s): "" read a metafor insctruction from a string. and change it to internal represt Ation. "Return read_from (tokenize (s) def tokenize (s):" convert a string into alist of tokens. "Return S. split () def read_from (tokens): "read an exp from a sequence of tokens. "If Len (tokens) = 0: Raise syntaxerror (" uncexpected EOF while reading. ") if is_setf (tokens): Return make_setf (tokens [0], tokens [2]) Elif is_inc (tokens): Return make_inc (tokens [0]) Elif is_dec (tokens): Return make_dec (tokens [0]) Elif is_goto (tokens): Return make_goto (tokens [1]) Elif is_con_goto (tokens): Return make_con_goto (tokens [1], tokens [3]) elif is_labeled_exp (tokens): Return make_labeled_exp (get_label (tokens [0]), read_from (tokens [1:]) else: Raise syntaxerror ("unexpected instructions: % s "% "". join (tokens )) #===================================================== ==================== def is_variable (token): If token. startswith ("X ") Or \ token. startswith ("Z") or \ token = "Y": Return true else: Return falsedef get_label (token): "token is like [a1]. we want get A1. "return Token. replace ('[',''). replace (']', '') # SETF def is_setf (tokens): If tokens [1] =" = "and Len (tokens) = 3: if is_variable (tokens [0]) and is_variable (tokens [2]): Return true else: Raise syntaxerror ("unexpected SETF instruction: % s" % "". join (tokens) els E: Return falsedef make_setf (des_var, src_var): return ["SETF", des_var, src_var] # Inc def is_inc (tokens): If Len (tokens) = 5 and tokens [1] = "=" \ and tokens [3] = "+" and tokens [4] = "1 ": if tokens [0] = tokens [2] and is_variable (tokens [0]): Return true else: Raise syntaxerror ("unexpected Inc instruction: % s" % "". join (tokens) else: Return falsedef make_inc (VAR): return ["Inc", VAR] # decd EF is_dec (tokens): If Len (tokens) = 5 and tokens [1] = "=" \ and tokens [3] = "-" And tokens [4] = "1 ": if tokens [0] = tokens [2] and is_variable (tokens [0]): Return true else: Raise syntaxerror ("unexpected dec instruction: % s" % "". join (tokens) else: Return falsedef make_dec (VAR): return ["dec", VAR] # gotodef is_goto (tokens): If Len (tokens) = 2 and tokens [0] = "to": Return true else: Return Falsedef make_goto (lable): return ["Goto", lable] # con_gotodef is_con_goto (tokens): If Len (tokens) = 6 and tokens [0] = "to" \ and tokens [2] = "if" and is_variable (tokens [3]) \ and tokens [4] = "! = "And tokens [5] =" 0 ": Return true else: Return falsedef make_con_goto (lable, VAR): return [" con_goto ", lable, vaR] # labeled expdef is_labeled_exp (tokens): Return tokens [0]. startswith ('[') def make_labeled_exp (Label, exp): return ["labeled_exp", label, exp] ####========================== codeseg ======== ================== def is_label (s): Return S = "labeled_exp" class codeseg: "to store internal Inst Ructions (exps ). n: The number of instructions. exps: the list of instructions. label_2_exps: A dict of {'label': num} pairs. "def _ init _ (self, exps): Self. N = Len (exps) self. exps = [] self. label_2_exp = {} for (I, e) in enumerate (exps): If is_label (E [0]): (_, label, exp) = E self. label_2_exp [label] = I self. exps. append (exp) else: Self. exps. append (e) def get_instruction_num (self, label): "retur N instruction num with the label. "If label in self. label_2_exp.keys (): return self. label_2_exp [label] # if there is no instruction with that label, # Return a instruction num that doesn't exit. else: return self. N def get_instruction (self, PC): return self. exps [PC] ###============================ env ==== ============================= class ENV (): "An enviromnet: A dict of {'var': Val} Paris, VaR's default Val UE is 0. "def _ init _ (self, argS): Self. pairs ={} for (I, V) in enumerate (ARGs): If I = 0: Self. pairs ['X'] = int (v) else: Self. pairs ['X' + STR (I + 1)] = int (v) def get_v (self, VAR): If VaR in self. pairs. keys (): return self. pairs [Var] else: Return 0 def set_v (self, VAR, value): Self. pairs [Var] = value ###============================ mainloop ====== ======================== def mainloop (codeseg, ENV): "PC refers The current instruction. "PC = 0 while Pc <codeseg. n: E = codeseg. get_instruction (PC) if E [0] = 'inc': (_, v) = E Env. set_v (v, Env. get_v (v) + 1) PC + = 1 Elif E [0] = 'dec ': (_, v) = E Env. set_v (v, max (Env. get_v (V)-1, 0) PC + = 1 Elif E [0] = 'setf': (_, x, y) = E Env. set_v (x, Env. get_v (y) PC + = 1 Elif E [0] = 'goto': (_, label) = e pc = codeseg. get_instruction_num (Label) Elif E [0] = "con_go To ": (_, label, VAR) = e val = env. get_v (VAR) If Val! = 0: Pc = codeseg. get_instruction_num (Label) else: PC + = 1 else: Raise syntaxerror ("unexpected instructions: % s" % "". join (E) return Env. get_v ('y ') ###===================================== repl ==================== ============================ def repl (input_prompt = 'inpututs> '): "A prompt-read-eval-print loop. "print_info () While true: program = get_prog_from_shell () If program = []: Return" Return successfully! "Codes = codeseg (PARSE (Program) Inputs = raw_input (input_prompt) ENV = ENV (inputs. split () print ("=>" + STR (mainloop (codes, ENV) def get_prog_from_shell (): program = [] exp = raw_input ("\ n \ ninput your program: \ n") while Exp: program. append (exp) exp = raw_input () return programdef print_info (): print ("============================================ =========================\ N "" * metafor 1.0, welecome to metafor sh Ell environment. * \ n "" * Author: Zhu zhaolong (zzljlu@gmail.com) * \ n "" ======================================== =================================\ N ") ###==================================== run ================ ====================== def run (filepath, prompt = 'metafor> '): "This function can run metafor program from file. "print_info () program = get_prog_from_file (filepath) parsed_prog = parse (Program) Codes = codeseg (parsed_prog) Inputs = raw_ I Nput (prompt) While true: ENV = ENV (inputs. split () print (mainloop (codes, ENV) Inputs = raw_input (prompt) If inputs = "exit": Return "Return successfully! "If inputs =" debug ": While true: Var = raw_input ('debug> ') If Var =" exit ": Return none print (Env. get_v (VAR) If inputs = "code": pprint (parsed_prog) return nonedef get_prog_from_file (filepath): F = open (filepath, "R") program = f. readlines () F. close () return program ###============================== test ======== =====================# y = x + x2test_file_1 = "metafor_test.mf" # Y = 2 * xtest_file_2 = "metafor_test2.mf" If _ name _ = "_ main __": run (test_file_1)