源码下载:https://files.cnblogs.com/files/heyang78/BasicInterpreter2-20200601-2.rar
用编程语言打印九九乘法表不难,用自编解释器运行自编脚本打印九九乘法表难度就多了那么一丢丢。本例就是讲述我编的这个程序:
输入脚本:
for x=1 to 9 for y=1 to x z=x*y print(x) print("*") print(y) print("=") print(z) print(" ") next newline() next print("end")
输出:
原文=for x=1 to 9 for y=1 to x z=x*y print(x) print("*") print(y) print("=") print(z) print(" ") next newline() next print("end") Tokens: Index Type No Text Type Desc ------------------------------------------------------------------------------------ 0 12 for for 1 6 x Variable 2 2 = = 3 4 1 DIGITAL 4 13 to to 5 4 9 DIGITAL 6 12 for for 7 6 y Variable 8 2 = = 9 4 1 DIGITAL 10 13 to to 11 6 x Variable 12 6 z Variable 13 2 = = 14 6 x Variable 15 19 * * 16 6 y Variable 17 7 print Function 18 0 ( ( 19 6 x Variable 20 1 ) ) 21 7 print Function 22 0 ( ( 23 5 "*" String 24 1 ) ) 25 7 print Function 26 0 ( ( 27 6 y Variable 28 1 ) ) 29 7 print Function 30 0 ( ( 31 5 "=" String 32 1 ) ) 33 7 print Function 34 0 ( ( 35 6 z Variable 36 1 ) ) 37 7 print Function 38 0 ( ( 39 5 " " String 40 1 ) ) 41 14 next next 42 7 newline Function 43 0 ( ( 44 1 ) ) 45 14 next next 46 7 print Function 47 0 ( ( 48 5 "end" String 49 1 ) ) 执行结果: 1*1=1 2*1=2 2*2=4 3*1=3 3*2=6 3*3=9 4*1=4 4*2=8 4*3=12 4*4=16 5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81 end
脚本稍微改改:
for x=1 to 9 for y=1 to x z=x*y print(y) print("*") print(x) print("=") print(z) print(" ") next newline() next print("end")
输出:
原文=for x=1 to 9 for y=1 to x z=x*y print(y) print("*") print(x) print("=") print(z) print(" ") next newline() next print("end") Tokens: 执行结果: 1*1=1 1*2=2 2*2=4 1*3=3 2*3=6 3*3=9 1*4=4 2*4=8 3*4=12 4*4=16 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81 end
核心类:
Token:
package com.heyang; public class Token { public static final int TYPE_OPEN_PARENTHESIS=0; // ( public static final int TYPE_CLOSE_PARENTHESIS=1; // ( public static final int TYPE_ASSIGN=2; // = public static final int TYPE_DIGITAL=4; // d+[.]?d* public static final int TYPE_STRING=5; // w+ public static final int TYPE_VARIABLE=6; // Variable public static final int TYPE_FUNCTION=7; // Function public static final int TYPE_EQUAL=8; // == public static final int TYPE_IF=9; // if public static final int TYPE_THEN=10; // then public static final int TYPE_LESSTHAN=10; // < public static final int TYPE_BIGGERTHAN=11; // > public static final int TYPE_FOR=12; // For public static final int TYPE_TO=13; // To public static final int TYPE_NEXT=14; // Next public static final int TYPE_LESSEUQALTHAN=15; // <= public static final int TYPE_BIGGEREQUALTHAN=16; // >= public static final int TYPE_PLUS=17; // + public static final int TYPE_MINUS=18; // - public static final int TYPE_MULTI=19; // * public static final int TYPE_DIVIDE=20; // / public static final int TYPE_ENDIF=21; // endif public static final int TYPE_ELSE=22; // else private int type; private String text; private int index;// Used to remember location public Token(char c,int type) { this.text=String.valueOf(c); this.type=type; } public Token(String word,int type) { this.text=word; this.type=type; } public String toString() { return String.format("token(text=%s,type=%s,index=%d)", text,getTypeStr(),index); } public String getTypeStr() { if(type==TYPE_OPEN_PARENTHESIS) { return "("; }else if(type==TYPE_CLOSE_PARENTHESIS) { return ")"; }else if(type==TYPE_ASSIGN) { return "="; }else if(type==TYPE_DIGITAL) { return "DIGITAL"; }else if(type==TYPE_STRING) { return "String"; }else if(type==TYPE_VARIABLE) { return "Variable"; }else if(type==TYPE_FUNCTION) { return "Function"; }else if(type==TYPE_EQUAL) { return "=="; }else if(type==TYPE_IF) { return "if"; }else if(type==TYPE_THEN) { return "then"; }else if(type==TYPE_LESSTHAN) { return "<"; }else if(type==TYPE_BIGGERTHAN) { return ">"; }else if(type==TYPE_FOR) { return "for"; }else if(type==TYPE_TO) { return "to"; }else if(type==TYPE_NEXT) { return "next"; }else if(type==TYPE_LESSEUQALTHAN) { return "<="; }else if(type==TYPE_BIGGEREQUALTHAN) { return ">="; }else if(type==TYPE_PLUS) { return "+"; }else if(type==TYPE_MINUS) { return "-"; }else if(type==TYPE_MULTI) { return "*"; }else if(type==TYPE_DIVIDE) { return "/"; }else if(type==TYPE_ENDIF) { return "endif"; }else if(type==TYPE_ELSE) { return "else"; } return null; } public int getType() { return type; } public void setType(int type) { this.type = type; } public String getText() { return text; } public void setText(String text) { this.text = text; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } }
Lexer:
package com.heyang; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; /** * Parse string to tokens * @author Heyang * */ public class Lexer { private List<Token> tokens; public Lexer(String text) { tokens = new ArrayList<Token>(); String swallowed = ""; for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (Character.isWhitespace(c)) { addTextToList(swallowed); swallowed=""; continue; } else if (c == '+') { addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_PLUS)); } else if (c == '-') { addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_MINUS)); }else if (c == '*') { addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_MULTI)); }else if (c == '/') { addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_DIVIDE)); }else if (c == '(') { addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_OPEN_PARENTHESIS)); } else if (c == ')') { addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_CLOSE_PARENTHESIS)); }else if (c == '>') { int next=i+1; if(next<text.length() && text.charAt(next)=='=') { // >= addTextToList(swallowed); swallowed=""; tokens.add(new Token(">=",Token.TYPE_BIGGEREQUALTHAN)); i++; }else { addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_BIGGERTHAN)); } }else if (c == '<') { int next=i+1; if(next<text.length() && text.charAt(next)=='=') { // <= addTextToList(swallowed); swallowed=""; tokens.add(new Token("<=",Token.TYPE_LESSEUQALTHAN)); }else { addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_LESSTHAN)); } } else if (c == '=') { int next=i+1; if(next<text.length() && text.charAt(next)=='=') { // == addTextToList(swallowed); swallowed=""; tokens.add(new Token("==",Token.TYPE_EQUAL)); i++; }else { // = addTextToList(swallowed); swallowed=""; tokens.add(new Token(c, Token.TYPE_ASSIGN)); } } else if(c == '"') { addTextToList(swallowed); swallowed=""; int idx=i+1; while(idx<text.length()) { char cEnd = text.charAt(idx); if (cEnd == '"') { break; } idx++; } String sub=text.substring(i, idx+1); tokens.add(new Token(sub, Token.TYPE_STRING)); i=idx; } else { swallowed += c; } } setTokenIndexes(); } private void addTextToList(String text) { if("if".equalsIgnoreCase(text)) { tokens.add(new Token(text, Token.TYPE_IF)); }else if("then".equalsIgnoreCase(text)) { tokens.add(new Token(text, Token.TYPE_THEN)); }else if("for".equalsIgnoreCase(text)) { tokens.add(new Token(text, Token.TYPE_FOR)); }else if("to".equalsIgnoreCase(text)) { tokens.add(new Token(text, Token.TYPE_TO)); }else if("next".equalsIgnoreCase(text)) { tokens.add(new Token(text, Token.TYPE_NEXT)); }else if("endif".equalsIgnoreCase(text)) { tokens.add(new Token(text, Token.TYPE_ENDIF)); }else if("else".equalsIgnoreCase(text)) { tokens.add(new Token(text, Token.TYPE_ELSE)); }else if(isFunction(text)) { tokens.add(new Token(text, Token.TYPE_FUNCTION)); }else if(isNumber(text)) { tokens.add(new Token(text, Token.TYPE_DIGITAL)); }else if(isVarable(text)) { tokens.add(new Token(text, Token.TYPE_VARIABLE)); } } private boolean isFunction(String text) { if("print".equalsIgnoreCase(text)) { return true; }else if("newline".equalsIgnoreCase(text)){ return true; } return false; } private boolean isNumber(String code) { final String patternStr = "(\d+)([.]?)(\d*)"; return Pattern.matches(patternStr, code); } private boolean isVarable(String code) { final String patternStr = "([a-zA-Z_])\w*"; return Pattern.matches(patternStr, code); } public void printTokens() { final String continuousStar = createRepeatedStr("-", 84); final String layout = "%-20s %-20s %-20s %-20s %s"; StringBuilder sb = new StringBuilder(); sb.append(String.format(layout, "Index", "Type No","Text","Type Desc"," ")); sb.append(continuousStar + " "); int index=0; for(Token token:tokens) { sb.append(String.format(layout, String.valueOf(index),String.valueOf(token.getType()), token.getText(),token.getTypeStr()," ")); index++; } System.out.println(sb.toString()); } private static String createRepeatedStr(String seed, int n) { return String.join("", Collections.nCopies(n, seed)); } public void setTokenIndexes() { int idx = 0; for (Token t : tokens) { idx++; t.setIndex(idx); } } public String getCompactText() { StringBuilder sb=new StringBuilder(); for (Token t : tokens) { sb.append(t.getText()); } return sb.toString(); } public List<Token> getTokens() { return tokens; } }
Interpreter:
package com.heyang; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; class LoopInfo{ public LoopInfo(String loopValueName,double limit,int startLocation) { this.loopValueName=loopValueName; this.limit=limit; this.startLocation=startLocation; } String loopValueName; double limit; int startLocation; } public class InterPreter { private List<Token> tokens; private int tokenIdx; private Map<String,Double> varMap;// 用于存放变量的变量表 private Stack<LoopInfo> loopStack;// 用于存放循环的栈 public InterPreter(List<Token> tokens){ this.tokens=tokens; this.tokenIdx=0; varMap=new HashMap<String,Double>(); loopStack=new Stack<LoopInfo>(); } // 执行程序 public void execute() throws Exception{ Token token; for(;;) { token=fetchToken(); if(token==null) { return; } if(token.getType()==Token.TYPE_VARIABLE) { returnToken(); doAssignment(); }else if(token.getType()==Token.TYPE_FUNCTION) { String fname=token.getText(); if("print".equalsIgnoreCase(fname)) { doPrint(); }if("newline".equalsIgnoreCase(fname)) { doNewline(); } }else if(token.getType()==Token.TYPE_IF) { returnToken(); doIf(); }else if(token.getType()==Token.TYPE_ELSE) { doElse(); }else if(token.getType()==Token.TYPE_FOR) { doFor(); }else if(token.getType()==Token.TYPE_NEXT) { doNext(); } } } private void doNext() throws Exception{ LoopInfo info=loopStack.pop(); String varName=info.loopValueName; Double value=loadVariable(varName); if(value==null) { throw new Exception("Not found variable:'"+varName); } double variableValue=value+1; saveVariable(info.loopValueName, variableValue); if(variableValue>info.limit) { // }else { loopStack.push(info); tokenIdx=info.startLocation; } } private void doFor() throws Exception{ Token token=fetchToken(); if(token.getType()!=Token.TYPE_VARIABLE) { throw new Exception("Expected:variable actual:"+token.getText()+" "+token); } String varibleName=token.getText(); token=fetchToken(); if(token.getType()==Token.TYPE_ASSIGN) { token=fetchToken(); if(token.getType()==Token.TYPE_DIGITAL) { double value=Double.parseDouble(token.getText()); saveVariable(varibleName, value); } }else { throw new Exception("Expected:'=' actual:"+token.getText()+" "+token); } token=fetchToken(); if(token.getType()!=Token.TYPE_TO) { throw new Exception("Expected:to actual:"+token.getText()+" "+token); } double limit; token=fetchToken(); if(token.getType()==Token.TYPE_DIGITAL) { limit=Double.parseDouble(token.getText()); }else if(token.getType()==Token.TYPE_VARIABLE) { String varName=token.getText(); Double value=loadVariable(varName); if(value==null) { throw new Exception("Not found variable:'"+varName+" token:"+token); } limit=value; }else { throw new Exception("Expected:digital/variable actual:"+token.getText()+" "+token); } double variableValue=loadVariable(varibleName); if(variableValue<=limit) { loopStack.push(new LoopInfo(varibleName,limit,tokenIdx)); }else { fetchToken(); } } private void doElse() throws Exception{ // 走到这里说明是if的肯定分支,因此需要跳过去 Token token; do { token=fetchToken(); }while(token.getType()!=Token.TYPE_ENDIF) ; } private void doIf() throws Exception{ Token token=fetchToken(); if(token.getType()!=Token.TYPE_IF) { throw new Exception("Expected:if actual:"+token.getText()+" "+token); } double left; token=fetchToken(); if(token.getType()==Token.TYPE_DIGITAL) { left=Double.parseDouble(token.getText()); }else if(token.getType()==Token.TYPE_VARIABLE) { String varName=token.getText(); Double value=loadVariable(varName); if(value==null) { throw new Exception("Not found variable:'"+varName+" token:"+token); } left=value; }else { throw new Exception("Expected:digital/variable actual:"+token.getText()+" "+token); } String oprand; token=fetchToken(); if(token.getType()!=Token.TYPE_EQUAL && token.getType()!=Token.TYPE_LESSTHAN && token.getType()!=Token.TYPE_LESSEUQALTHAN && token.getType()!=Token.TYPE_BIGGERTHAN && token.getType()!=Token.TYPE_BIGGEREQUALTHAN ) { throw new Exception("Expected:comparison symbol, actual:"+token.getText()+" "+token); } oprand=token.getText(); double right; token=fetchToken(); if(token.getType()==Token.TYPE_DIGITAL) { right=Double.parseDouble(token.getText()); }else if(token.getType()==Token.TYPE_VARIABLE) { String varName=token.getText(); Double value=loadVariable(varName); if(value==null) { throw new Exception("Not found variable:'"+varName+" token:"+token); } right=value; }else { throw new Exception("Expected:digital/variable actual:"+token.getText()+" "+token); } if(compare(left,oprand,right)) { token=fetchToken(); if(token.getType()!=Token.TYPE_THEN) { throw new Exception("Expected:'then' actual:"+token.getText()+" "+token); } }else { do { token=fetchToken(); }while(token.getType()!=Token.TYPE_ENDIF && token.getType()!=Token.TYPE_ELSE) ; } } private boolean compare(double vLeft,String oprand,double vRight){ if("==".equals(oprand)) { return vLeft==vRight; }else if(">".equals(oprand)) { return vLeft>vRight; }else if(">=".equals(oprand)) { return vLeft>=vRight; }else if("<".equals(oprand)) { return vLeft<vRight; }else if("<=".equals(oprand)) { return vLeft<=vRight; }else { return false; } } private void doNewline() throws Exception{ Token token=fetchToken(); if(token.getType()!=Token.TYPE_OPEN_PARENTHESIS) { throw new Exception("Expected:'(' actual:"+token.getText()+" "+token); } System.out.println(); token=fetchToken(); if(token.getType()!=Token.TYPE_CLOSE_PARENTHESIS) { throw new Exception("Expected:')' actual:"+token.getText()+" "+token); } } private void doPrint() throws Exception{ Token token=fetchToken(); if(token.getType()!=Token.TYPE_OPEN_PARENTHESIS) { throw new Exception("Expected:'(' actual:"+token.getText()+" "+token); } token=fetchToken(); if(token.getType()==Token.TYPE_STRING) { String str=token.getText(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if(c!='"'){ System.out.print(c); } } }else if(token.getType()==Token.TYPE_VARIABLE) { String varName=token.getText(); Double value=loadVariable(varName); if(value==null) { throw new Exception("Not found variable:'"+varName+" token:"+token); } try { int i=value.intValue(); System.out.print(i); }catch(Exception ex) { System.out.print(value); } } token=fetchToken(); if(token.getType()!=Token.TYPE_CLOSE_PARENTHESIS) { throw new Exception("Expected:')' actual:"+token.getText()+" "+token); } } // 赋值 private void doAssignment() throws Exception{ Token token=fetchToken(); if(token.getType()!=Token.TYPE_VARIABLE) { throw new Exception("Expected:variable actual:"+token.getText()+" "+token); } String varName=token.getText(); token=fetchToken(); if(token.getType()!=Token.TYPE_ASSIGN) { throw new Exception("Expected:= actual:"+token.getText()+" "+token); } double varValue=parse_expression(); saveVariable(varName,varValue); } // 解析表达式 private double parse_expression() throws Exception{ double left,right; Token currentToken; left=parse_term(); for(;;) { currentToken=fetchToken(); if(currentToken==null) { return left; } if(currentToken.getType()!=Token.TYPE_PLUS && currentToken.getType()!=Token.TYPE_MINUS) { returnToken(); break; } right=parse_term(); if(currentToken.getType()==Token.TYPE_PLUS) { left+= right; }else if(currentToken.getType()==Token.TYPE_MINUS) { left-= right; }else { returnToken(); } } return left; } // 解析乘除 private double parse_term() throws Exception{ double left,right; Token currentToken; left=parse_primary_exp(); for(;;) { currentToken=fetchToken(); if(currentToken==null) { return left; } if(currentToken.getType()!=Token.TYPE_MULTI && currentToken.getType()!=Token.TYPE_DIVIDE) { returnToken(); break; } right=parse_primary_exp(); if(currentToken.getType()==Token.TYPE_MULTI) { left*= right; }else if(currentToken.getType()==Token.TYPE_DIVIDE) { left/= right; } } return left; } // 解析数字/变量/表达式 private double parse_primary_exp() throws Exception{ Token token; double retval=0.0; token=fetchToken(); if(token==null) { return 0; } if(token.getType()==Token.TYPE_DIGITAL) { retval= Double.parseDouble(token.getText()); return retval; }else if(token.getType()==Token.TYPE_VARIABLE) { String varName=token.getText(); Double d=loadVariable(varName); if(d==null) { throw new Exception("Not found variable:'"+varName+" token:"+token); } return d; }else if(token.getType()==Token.TYPE_OPEN_PARENTHESIS){ retval=parse_expression(); token=fetchToken(); if(token==null) { return retval; } if(token.getType()!=Token.TYPE_CLOSE_PARENTHESIS) { throw new Exception("missing )"); } return retval; }else { throw new Exception(token+" should be a digital."); } } // 取标记 private Token fetchToken() { if(tokenIdx>=tokens.size()) { return null; }else { Token t=tokens.get(tokenIdx); tokenIdx++; return t; } } // 退回标记 private void returnToken() { if(tokenIdx>0) { tokenIdx--; } } // 保存变量进变量表 private void saveVariable(String name,double value) { varMap.put(name, value); } // 取得变量值从变量表 private Double loadVariable(String name) { if(varMap.containsKey(name)) { return varMap.get(name); }else { return null; } } }
EnterPoint:
package com.heyang; import com.heyang.util.BracketChecker; import com.heyang.util.CommonUtil; import com.heyang.util.Renderer; public class EntryPoint { public static void main(String[] args) { try { // Read context from file String text=CommonUtil.readTextFromFile("C:\hy\files\basic\12.basic"); System.out.println("原文="+text); // Is brackets balanced BracketChecker checker=new BracketChecker(); boolean isBalanced=checker.isBalanced(text); if(isBalanced==false) { System.out.println(Renderer.paintBrown(checker.getErrMsg())); return; } // lex text to tokens Lexer lex=new Lexer(text); System.out.println("Tokens:"); lex.printTokens(); // Execute System.out.println("执行结果: "); InterPreter ip=new InterPreter(lex.getTokens()); ip.execute(); }catch(Exception ex) { System.out.println(Renderer.paintBrown(ex.getMessage())); ex.printStackTrace(); } } }
今天是六一儿童节,我也因为此程序的研制成功感受到了儿童那纯洁天真的快乐。
--2020年6月1日--