📄 mynalex.java
字号:
package MyNa.utils;
import java.util.Hashtable;
import java.util.Stack;
/*
MyNaLex is intended as a _simple_ lexical-analyzer for web pages, which
goes to a bit of trouble to reduce garbage-collection and which shows
some of the use of regular expressions in manually implemented
minilanguages. It's specialized, in that it looks for HTML-style tokens
of the form <myna:___> and matching </myna:____>; this makes some small
optimization possible, in that if there's no "<myna:" string in a page
we can skip it altogether (and if there is, we can start there.)
A note on garbage collection: most garbage collection in this context
is required because of dynamically created strings. This class doesn't
create strings dynamically except in
(a) context() (and getStr) which are only for error messages,
(b) getTokenString(), which can be used by other classes but here is
used only within debugging code,
(c) getDef, which has to produce strings for consumption by other
classes...from <myna:tag name1='val1' name2 = "val2" name3= |val3| >,
we have to produce a hashtable which will contain 6 strings, one for each
name and one for each value. Counting the hashtable, that's seven dynamically
created objects...that's hard to avoid, though we could work at making sure
we're not re-creating objects which already exist. Incidentally, we don't
create empty hashtables, preferring the "null" hashtable for <myna:tag>
For the most part, simple regular expressions become simple code, as you'll
see below. The top-level "getToken()" method has to have a state by which it
remembers, in the midst of matching a complex expression
*/
public class MyNaLex {
public static final int noToken = -1;
public static final int textToken = 0;
public static final int mynaToken = 1;
public static final int endMynaToken = 2;
public static final int endAllToken = 3;
public static String defaultDelim = "|";
private StringBuffer sBuff=null;
private String theString=null; // theString is a reference to sBuff.
private int sBuffLength;
private int curLoc; // curLoc will shift through sBuff as we read.
private int tokenType; // these four properties _are_ current token.
private int tokenStart;
private int tokenEnd;
private Hashtable tokenProps=null;
public static String[]tokTypeNames= //noToken==-1
new String[]{"noToken","textToken",
"mynaToken","endMynaToken","endAllToken"};
private int pushbackTokenType; // these four are a token which has
private int pushbackTokenStart; // been recognized but not yet yielded
private int pushbackTokenEnd; // as in "urble<myna:SUBST>..." where
private Hashtable pushbackTokenProps; // we produce "urble", save SUBST.
private String currentDelimiter; // null outside of range.
private Stack delimStack; // nested ranges, multiple delimiters.
Logger lg; private static boolean logOn=false;
public MyNaLex(StringBuffer sB){
lg=new Logger();
sBuff=sB;
if(sBuff==null)
{lg.logIt("null string buff for MyNaLex"); sBuff=new StringBuffer();}
theString=sBuff.toString();
// lg.logIt("**********MyNaLex invoked on*********************\n"+
// theString+"\n**********MyNaLex*********************");
sBuffLength=theString.length();
curLoc=0;
tokenStart=0; tokenEnd=0; tokenType=noToken; tokenProps=null;
currentDelimiter=null; delimStack=null;
pushbackTokenType=noToken;
}
public int getTokenStart(){return tokenStart;}
public int getTokenEnd(){return tokenEnd;}
public int getTokenType(){return tokenType;}
public Hashtable getTokenProps(){return tokenProps;}
public int setToken(int lo,int hi,int tType,Hashtable tProps){
tokenStart=lo; tokenEnd=hi; tokenType=tType; tokenProps=tProps;
// delimiters are set by mynaToken, unset by endMynaToken
if(tokenType==endMynaToken){
if(delimStack==null || delimStack.empty())
currentDelimiter=null;
else currentDelimiter=(String)delimStack.pop();
}
else if(tokenType==mynaToken){
if(currentDelimiter!=null){ // this only happens in nested <myna:
if(delimStack==null)delimStack=new Stack();
delimStack.push(currentDelimiter);
}
String delim;
if(tProps!=null && null!=(delim=(String)tProps.get("delim")))
currentDelimiter=delim;
else currentDelimiter=defaultDelim;
}
return tokenType;
}
public String getTokenString(){
if(tokenStart<tokenEnd)
return theString.substring(tokenStart,tokenEnd);
if(tokenStart==tokenEnd)return "";
lg.logIt("tokenStart>tokenEnd,"+tokenStart+">"+tokenEnd);
return "";
}
public int getToken(){tokenProps=null;
if(logOn)lg.logIt("getToken called with curLoc="+curLoc+
", prevTokType="+tokTypeNames[1+tokenType]+
", prevTokVal:\n"+
(tokenType<0?"":getTokenString()+
",context="+context()));
if(pullforthToken())return tokenType; // previously matched
if(curLoc>=sBuffLength)return tokenType=endAllToken; // nothing there
if(curLoc==0){// initial call manual optimization (check empty case)
// does not affect the actual result
int firstLoc=theString.indexOf("<myna:");
if(firstLoc<0) return setToken(0,curLoc=sBuffLength,textToken,null);
}
int oldCurLoc=curLoc; // location at start of getToken1 call
while(curLoc<sBuffLength){
char c=sBuff.charAt(curLoc); // another manual optimization;
if(c!='<' && // if these tests fail, so would matching below.
(currentDelimiter==null || c!= currentDelimiter.charAt(0)) )
{curLoc++; continue;} // end manual optimization section
int startMatch=curLoc; // location as we start trying to match.
if(getMynaToken())
return pushbackToken(oldCurLoc,startMatch,textToken,null);
if(getEndMynaToken())
return pushbackToken(oldCurLoc,startMatch,textToken,null);
if(null!=currentDelimiter && getStr(currentDelimiter))
return setToken(oldCurLoc,startMatch,textToken,null);
curLoc++;
}
return setToken(oldCurLoc,sBuffLength,textToken,null);
}
public boolean getLetter(){
if(curLoc>=sBuffLength) return false;
char c=sBuff.charAt(curLoc);
if( ('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| (c=='_' || c=='#') )
{curLoc++; return true;}
return false;
}
public boolean getChar(char c){
if(curLoc>=sBuffLength) return false;
if(c!=sBuff.charAt(curLoc))return false;
curLoc++;
return true;
}
public boolean getCharRange(char lo, char hi){
if(curLoc>=sBuffLength) return false;
char c=sBuff.charAt(curLoc);
if(c<lo || c>hi)return false;
curLoc++;
return true;
}
public boolean getWhiteSpace(){
if(curLoc>=sBuffLength) return false;
char c=sBuff.charAt(curLoc);
if(c>' ')return false;
while((++curLoc<sBuffLength) && (' '>sBuff.charAt(curLoc)));
return true;
}
public boolean getStr(String S){
if(theString==null){lg.logIt("null string in getStr"); return false;}
if(S==null){lg.logIt("null match for getStr"); return false;}
if(!theString.startsWith(S,curLoc))return false;
curLoc+=S.length();
return true;
}
public boolean getId(){
int start=curLoc;
if(!getLetter())return false;
while(getLetter()); // curLoc now points past the end of the id.
return true;
}
public boolean getQStr(){
int start=curLoc;
if(curLoc>=sBuffLength)return false;
char qChar= sBuff.charAt(curLoc++);
while (curLoc<sBuffLength && qChar!=sBuff.charAt(curLoc))curLoc++;
if(curLoc>=sBuffLength){curLoc=start; return false;}
curLoc++; // step past the matching quote character;
return true;
}
public boolean getDef(){
int startId=curLoc;
if(!getId())return false;
int endId=curLoc; // endId points to the '=', or should
getWhiteSpace();
if(!getChar('=')){curLoc=startId; return false;}
getWhiteSpace();
if(!getQStr()){curLoc=startId; return false;}
if(tokenProps==null)tokenProps=new Hashtable(1);
tokenProps.put(theString.substring(startId,endId),
theString.substring(endId+2,curLoc-1));
return true;
}
public boolean getMynaToken(){ // "<myna:" id [" "+ (def " "*)*] ">"
if(logOn)lg.logIt("gMT: "+context());
int startTok=curLoc;
if(!getStr("<myna:")){ return false;}
int startId=curLoc;
if(!getId()){curLoc=startTok; return false;}
int endId=curLoc;
if(getWhiteSpace())
while(getDef())
while(getWhiteSpace());
if(!getChar('>')){curLoc=startTok; lg.logIt("gMT failed 1"+context()); return false;}
setToken(startTok+1,endId,mynaToken,tokenProps); // skip "<"
if(logOn)lg.logIt("gMTend: tokenStart="+tokenStart+", tokenEnd="+tokenEnd+
",currentDelim="+currentDelimiter+
",tokenType="+tokTypeNames[1+tokenType]+"context="+context());
return true;
}
public boolean getEndMynaToken(){
int startTok=curLoc;
if(!getStr("</myna:")) return false;
int startId=curLoc;
if(!getId()){ curLoc=startTok; return false;}
int endId=curLoc;
while(getChar(' '));
if(!getChar('>')){ curLoc=startTok; return false;}
setToken(2+startTok, endId,endMynaToken,tokenProps); // skip "</"
return true;
}
public String context(){ // used in error messages.
if(curLoc>=sBuffLength)
return "end of Buffer:\n ["+getStr(sBuffLength-20,sBuffLength);
int numLines=0; int nl=0;
for(int i=0;i<curLoc;i++)
if(theString.charAt(i)=='\n'){numLines++;nl=i;}
String msg= "MyNaTok Context:\n|line "+numLines+", char "+(curLoc-nl)
+"\n|"+getStr(nl,curLoc);
for(int i=curLoc;i<sBuffLength && theString.charAt(i)!='\n';i++)
msg+=theString.charAt(i);
msg+="\n|";
int lastC=curLoc-1;
for(int i=nl;i<lastC;i++)msg+=".";
msg+="*";
return msg;
}
public String getStr(int lo, int hi){
if(lo>=hi)return "";
if(lo>=sBuffLength || hi<0)return "";
if(lo<0)lo=0;if(hi>sBuffLength)hi=sBuffLength;
return theString.substring(lo,hi);
}
public void pushbackToken(){
pushbackTokenStart=tokenStart;
pushbackTokenType=tokenType;
pushbackTokenEnd=tokenEnd;
pushbackTokenProps=tokenProps;
tokenProps=null;
}
public int pushbackToken(int lo,int hi,int tt,Hashtable tp){
pushbackToken();
return setToken(lo,hi,tt,tp);
}
public boolean pullforthToken(){
if(pushbackTokenType==noToken)return false;
setToken(pushbackTokenStart,pushbackTokenEnd,
pushbackTokenType,pushbackTokenProps);
pushbackTokenType=noToken; // mark as unavailable.
return true;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -