📄 parsesubst.java
字号:
package MyNa.utils;
import java.io.*;
import java.util.*;
import java.sql.SQLException;
/* *****************************************************************
The ParseSubst class introduces minilanguage processing at the
parse-tree level, but in this case we have only a list of lists.
Consider the sample input string inStr=
"The time <myna:SUBST> has come, |speaker1| said, to |communicateAbout|
many </myna:SUBST> things, of <myna:SUBST delim="$"> $topicA$ and $topicB$,
of $topicC$ and $topicD$</myna:SUBST>; of why the sea is boiling hot, and
whether pigs have wings."
If we now say
ParseSubst pS=new ParseSubst(new StringBuffer(inStr));
we are breaking the inStr up as if it were labelled as follows:
<ROOT>
<TEXT>The time </TEXT>
<myna:SUBST>
<TEXT> has come, </TEXT>
<DELIM>speaker1</DELIM>
<TEXT> said, to </TEXT>
<DELIM>communicateAbout</DELIM>
<TEXT> many </TEXT>
</myna:SUBST>
<TEXT> things, of </TEXT>
<myna:SUBST delim="$">
<TEXT> </TEXT>
<DELIM>topicA</DELIM>
<TEXT> and </TEXT>
<DELIM>topicB</DELIM>
<TEXT>, of </TEXT>
<DELIM>topicC</DELIM>
<TEXT> and </TEXT>
<DELIM>topicD</DELIM>
</myna:SUBST>
<TEXT>; of why the sea is boiling hot, and
whether pigs have wings.</TEXT>
We now see that
pS.theTree.numChildren()==5
pS.getText(pS.theTree.child(1).child(0))==" has come, "
and in an Env where speaker1="the Walrus", we see
pS.getVal(pS.theTree.child(1).child(1))=="the Walrus"
and so on. Actually, theTree is not public, but this should
indicate the structures generated. Note that although
pS.theTree.child(1).child(0).getText()==" has come, "
would be more intuitive, it isn't true: the tree does not
know how to interpret itself as a text. The ParseSubst object
has additional data, namely the input stringbuffer and the env.
We work with a grammar which is not completely context-free:
Root ::= T (Subst T)* ;
Subst ::= SubstTok(tok,d) T (Delim(d) T)* EndTok(tok);
Delim(d) ::= d T d;
Read this as follows: The input contains initial text, and then
zero or more repetitions of a Subst followed by more text. A
Subst begins with a SubstTok (defining the actual token and delim
to be used), and it ends with a matching endTok (thus <myna:SUBST> will
end with </myna:SUBST> but <myna:SUBSTROW> will end with </myna:SUBSTROW>.
In between we have initial text, then zero or more repetitions of
a Delim followed by more text. Finally, we see that a Delim begins
and ends with the defined delimiter, and has plain text in between.
***************************************************************** */
public class ParseSubst {
StringBuffer theBuff; String theString;
MyNaLex lex=null;
ParseTree theTree=null;
Env theEnv=null; RowSequence theRows=null;
boolean substFailure;
StringBuffer outBuff;
Logger lg;
public String toString(Env env) throws ParseSubstException{
theEnv=env;
outBuff=new StringBuffer();
toStringBuffer(theTree);
return outBuff.toString();
}
public String toString(RowSequence rows) throws ParseSubstException{
theRows=rows;
if(null==rows)throw new ParseSubstException("empty RowSequence in toString");
theEnv=theRows.getRow();
outBuff=new StringBuffer();
toStringBuffer(theTree);
return outBuff.toString();
}
public String getText(ParseTree T){
int low=T.getLow(); int high=T.getHigh();
if(low>=high)return "";
return theString.substring(low,high);
}
public String getVal(ParseTree T){
int low=T.getLow(); int high=T.getHigh();
if(low>=high)return "";
return theEnv.getStr(theString.substring(low,high));
}
String treeReport(ParseTree T){
if(T==null)return "<NULLTREE>";
String tag=T.getTagName();
String S="<"+tag+">";
for(int i=0;i<T.numChildren();i++)
S+=treeReport(T.child(i));
if("TEXT".equals(tag))S+=getText(T);
S+="</"+tag+">";
return S;
}
String treeReport(){return treeReport(theTree);}
public void toStringBuffer(ParseTree T) throws ParseSubstException{
if(T==null)return;
int N=T.numChildren();
String tag=T.getTagName();
if(tag.equals("ROOT"))
for(int i=0;i<N;i++)
toStringBuffer(T.child(i));
else if(tag.equals("TEXT"))
outText(T);
else if(tag.startsWith("myna:"))
substStringBuffer(tag,T);
else throw new ParseSubstException("expected ROOT or 'myna:', found "+tag);
}
public void newQuery(ParseTree T) throws ParseSubstException{
Hashtable H=T.getProps();
if(H==null)return;
String theOp=(String)H.get("dbOperation");
if(theOp==null)return;
theEnv.addHashtable(H);
DBHandler theDBHandler=(DBHandler)theEnv.get("dbHandler");
if(theDBHandler==null)
throw new ParseSubstException("no dbhandler for op "+theOp);
try{
theRows=theDBHandler.getQueryRows(theEnv);
}catch(SQLException ex){
throw new ParseSubstException("dbHandler for op "+theOp+
"failed with SQLException "+ex);
}
}
public void doDef(ParseTree T) throws ParseSubstException{
Hashtable H=T.getProps(); String name;
if(H==null || null==(name=(String)H.get("name")))
throw new ParseSubstException("SUBSTDEF needs a 'name' property");
StringBuffer valBuff=new StringBuffer();
outSubVals(T,valBuff);
theEnv.put(name,valBuff.toString());
}
public void substStringBuffer(String tag,ParseTree T) throws ParseSubstException{
newQuery(T);
if(tag.equals("myna:SUBST")) outSubVals(T);
else if(tag.equals("myna:SUBSTROW")){
if(substFailure || null==theRows ||
!theRows.next())
{substFailure=true; return;}
else {
theEnv=theRows.getRow();
outSubVals(T);
}
} else if(tag.equals("myna:SUBSTERR")){
if(substFailure) outSubVals(T);
} else if(tag.equals("myna:SUBSTROWLIST")){
if(substFailure || null==theRows)return;
while(theRows.next()){
theEnv=theRows.getRow();
outSubVals(T);
}
} else if(tag.equals("myna:SUBSTDEF"))doDef(T);
else throw new ParseSubstException("unrecognized tag "+tag);
}
public void outSubVals(ParseTree T){
int N=T.numChildren();
for(int i=0;i<N;i++)
if(T.child(i).getTagName().equals("DELIM"))
outVal(T.child(i));
else outText(T.child(i));
}
public void outText(ParseTree T){
int low=T.getLow(); int high=T.getHigh();
if(low>=high)return;
outBuff.append(theString.substring(low,high));
}
public void outVal(ParseTree T){
int low=T.getLow(); int high=T.getHigh();
if (low>=high)return;
outBuff.append(theEnv.getStr(theString.substring(low,high)));
}
/* copy the three "out" functions for generating output
text and values, give them a StringBuffer as explicit param;
now they can be used to generate definitions which are not
sent to the output but placed in the Env. */
public void outSubVals(ParseTree T,StringBuffer sB){
int N=T.numChildren();
for(int i=0;i<N;i++)
if(T.child(i).getTagName().equals("DELIM"))
outVal(T.child(i),sB);
else outText(T.child(i),sB);
}
public void outText(ParseTree T,StringBuffer sB){
int low=T.getLow(); int high=T.getHigh();
if(low>=high)return;
sB.append(theString.substring(low,high));
}
public void outVal(ParseTree T,StringBuffer sB){
int low=T.getLow(); int high=T.getHigh();
if (low>=high)return;
sB.append(theEnv.getStr(theString.substring(low,high)));
}
public String context(int n,int m){
if(n>=m)return "[no context:"+n+">="+m+"]";
if(n>=theString.length()) return
"[no context: "+n+">= string length "+theString.length()+"]";
if(m>=theString.length())return theString.substring(n,theString.length());
return theString.substring(n,m);
}
public ParseSubst(StringBuffer sB) throws ParseSubstException{
if(sB==null)throw new ParseSubstException("can't parse null string buffer");
theBuff=sB;
theString=sB.toString();
lex=new MyNaLex(theBuff);
lg=new Logger();
theTree=parseRoot();
// lg.logIt(treeReport(theTree));
substFailure=false;
}
// Root ::= (T | Subst)* ;
public ParseTree parseRoot() throws ParseSubstException{
ParseTree root=new ParseTree("ROOT",0,theString.length());
int tokType=lex.getToken();
while(MyNaLex.endAllToken!=tokType){
if(tokType==MyNaLex.noToken)
throw new ParseSubstException("ERROR: Bad token at parseRoot:\n "
+lex.context());
int lo=lex.getTokenStart(); int hi=lex.getTokenEnd();
if(tokType==MyNaLex.textToken)
root.addChild(new ParseTree("TEXT",lo,hi));
else if(tokType==MyNaLex.mynaToken)
root.addChild(parseSub(lo,hi));
else throw new ParseSubstException("ERROR: Bad token at beginning:\n "
+lex.context());
tokType=lex.getToken();
}
return root;
}
// Subst ::= SubstTok(tok,d) T (Delim(d) T)* EndTok(tok);
public ParseTree parseSub(int lo, int hi) throws ParseSubstException{
// the current token is a mynaToken, or we wouldn't be here.
Hashtable tokProps=lex.getTokenProps();
String tagName=theString.substring(lo,hi);
ParseTree pS=new ParseTree(tagName,lo,hi,tokProps);
int tokType;
while(MyNaLex.textToken==(tokType=lex.getToken())){
pS.addChild(new ParseTree("TEXT",
lex.getTokenStart(),
lex.getTokenEnd()));
if(MyNaLex.textToken!=(tokType=lex.getToken()))break;
// textToken values are TEXT, DELIM alternately.
pS.addChild(new ParseTree("DELIM",
lex.getTokenStart(),
lex.getTokenEnd()));
}
String closeTag=null;
if(tokType!=MyNaLex.endMynaToken
|| !tagName.equals(closeTag=lex.getTokenString()) )
throw new ParseSubstException("ERROR: <XMP>expecting ["+tagName+
"], found ["+closeTag+"]\n"+
lex.context()+"</XMP>");
return pS;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -