提交 3acbf0c2 authored 作者: Thomas Mueller's avatar Thomas Mueller

The BNF parser now uses the visitor pattern.

上级 d60981be
...@@ -18,7 +18,8 @@ Change Log ...@@ -18,7 +18,8 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>The methods of the CloseListener are added to the Trigger interface. <ul><li>The BNF parser now uses the visitor pattern.
</li><li>The methods of the CloseListener are added to the Trigger interface.
The interface CloseListener is removed. The interface CloseListener is removed.
</li><li>Converting a UUID to bytes was incorrect. Because of that, updatable result sets on </li><li>Converting a UUID to bytes was incorrect. Because of that, updatable result sets on
tables with UUID primary key did not work. tables with UUID primary key did not work.
......
...@@ -14,9 +14,7 @@ import java.sql.ResultSet; ...@@ -14,9 +14,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Random;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.h2.server.web.DbContextRule; import org.h2.server.web.DbContextRule;
import org.h2.tools.Csv; import org.h2.tools.Csv;
import org.h2.util.New; import org.h2.util.New;
...@@ -29,8 +27,6 @@ import org.h2.util.Utils; ...@@ -29,8 +27,6 @@ import org.h2.util.Utils;
*/ */
public class Bnf { public class Bnf {
private final Random random = new Random();
/** /**
* The rule map. The key is lowercase, and all spaces * The rule map. The key is lowercase, and all spaces
* are replaces with underscore. * are replaces with underscore.
...@@ -45,10 +41,6 @@ public class Bnf { ...@@ -45,10 +41,6 @@ public class Bnf {
private ArrayList<RuleHead> statements; private ArrayList<RuleHead> statements;
private String currentTopic; private String currentTopic;
Bnf() {
random.setSeed(1);
}
/** /**
* Create an instance using the grammar specified in the CSV file. * Create an instance using the grammar specified in the CSV file.
* *
...@@ -80,10 +72,6 @@ public class Bnf { ...@@ -80,10 +72,6 @@ public class Bnf {
return head; return head;
} }
public Random getRandom() {
return random;
}
private void parse(Reader csv) throws SQLException, IOException { private void parse(Reader csv) throws SQLException, IOException {
Rule functions = null; Rule functions = null;
statements = New.arrayList(); statements = New.arrayList();
...@@ -132,54 +120,13 @@ public class Bnf { ...@@ -132,54 +120,13 @@ public class Bnf {
addFixedRule("@close_bracket@", RuleFixed.CLOSE_BRACKET); addFixedRule("@close_bracket@", RuleFixed.CLOSE_BRACKET);
} }
/** public void visit(BnfVisitor visitor, String s) {
* Get the HTML railroad for a given syntax. this.syntax = s;
* tokens = tokenize();
* @param bnf the syntax index = 0;
* @return the HTML formatted railroad Rule rule = parseRule();
*/ rule.setLinks(ruleMap);
public String getRailroadHtml(String bnf) { rule.accept(visitor);
bnf = StringUtils.replaceAll(bnf, "\n ", " ");
String[] syntaxList = StringUtils.arraySplit(bnf, '\n', true);
StringBuilder buff = new StringBuilder();
for (String s : syntaxList) {
this.syntax = s;
tokens = tokenize();
index = 0;
Rule rule = parseRule();
rule.setLinks(ruleMap);
String html = rule.getHtmlRailroad(this, false);
html = StringUtils.replaceAll(html, "</code></td><td class=\"d\"><code class=\"c\">", " ");
if (buff.length() > 0) {
buff.append("<br />");
}
buff.append(html);
}
return buff.toString();
}
/**
* Get the HTML documentation for a given syntax.
*
* @param bnf the BNF syntax
* @return the HTML formatted text
*/
public String getSyntaxHtml(String bnf) {
bnf = StringUtils.replaceAll(bnf, "\n ", "\n");
StringTokenizer tokenizer = getTokenizer(bnf);
StringBuilder buff = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
String s = tokenizer.nextToken();
if (s.length() == 1 || StringUtils.toUpperEnglish(s).equals(s)) {
buff.append(StringUtils.xmlText(s));
continue;
}
buff.append(getLink(s));
}
String s = buff.toString();
// ensure it works within XHTML comments
s = StringUtils.replaceAll(s, "--", "&#45;-");
return s;
} }
/** /**
...@@ -188,7 +135,7 @@ public class Bnf { ...@@ -188,7 +135,7 @@ public class Bnf {
* @param token the token * @param token the token
* @return the rule map key * @return the rule map key
*/ */
static String getRuleMapKey(String token) { public static String getRuleMapKey(String token) {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
for (char ch : token.toCharArray()) { for (char ch : token.toCharArray()) {
if (Character.isUpperCase(ch)) { if (Character.isUpperCase(ch)) {
...@@ -201,39 +148,13 @@ public class Bnf { ...@@ -201,39 +148,13 @@ public class Bnf {
} }
/** /**
* Get the HTML link for the given token, or the token itself if no link * Get the rule head for the given title.
* exists.
* *
* @param token the token * @param title
* @return the HTML link * @return the rule head, or null
*/ */
String getLink(String token) { public RuleHead getRuleHead(String title) {
RuleHead found = null; return ruleMap.get(title);
String key = getRuleMapKey(token);
for (int i = 0; i < token.length(); i++) {
String test = StringUtils.toLowerEnglish(key.substring(i));
RuleHead r = ruleMap.get(test);
if (r != null) {
found = r;
break;
}
}
if (found == null) {
return token;
}
String page = "grammar.html";
if (found.getSection().startsWith("Data Types")) {
page = "datatypes.html";
} else if (found.getSection().startsWith("Functions")) {
page = "functions.html";
} else if (token.equals("@func@")) {
return "<a href=\"functions.html\">Function</a>";
} else if (found.getRule() instanceof RuleFixed) {
return found.getRule().getHtmlRailroad(this, false);
}
String link = found.getTopic().toLowerCase().replace(' ', '_');
link = page + "#" + StringUtils.urlEncode(link);
return "<a href=\"" + link + "\">" + token + "</a>";
} }
private Rule parseRule() { private Rule parseRule() {
...@@ -396,7 +317,13 @@ public class Bnf { ...@@ -396,7 +317,13 @@ public class Bnf {
return statements; return statements;
} }
private StringTokenizer getTokenizer(String s) { /**
* Get the tokenizer for the given syntax.
*
* @param s the syntax
* @return the tokenizer
*/
public StringTokenizer getTokenizer(String s) {
return new StringTokenizer(s, " [](){}|.,\r\n<>:-+*/=<\">!'$", true); return new StringTokenizer(s, " [](){}|.,\r\n<>:-+*/=<\">!'$", true);
} }
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.bnf;
import java.util.ArrayList;
/**
* The visitor interface for BNF rules.
*/
public interface BnfVisitor {
/**
* Visit a rule element.
*
* @param keyword whether this is a keyword
* @param name the element name
* @param link the linked rule if it's not a keyword
*/
void visitRuleElement(boolean keyword, String name, Rule link);
/**
* Visit a repeat rule.
*
* @param comma whether the comma is repeated as well
* @param rule the element to repeat
*/
void visitRuleRepeat(boolean comma, Rule rule);
/**
* Visit a fixed rule.
*
* @param type the type
*/
void visitRuleFixed(int type);
/**
* Visit a rule list.
*
* @param or true for OR, false for AND
* @param list the rules
*/
void visitRuleList(boolean or, ArrayList<Rule> list);
/**
* Visit an optional rule.
*
* @param rule the rule
*/
void visitRuleOptional(Rule rule);
}
...@@ -20,22 +20,6 @@ public interface Rule { ...@@ -20,22 +20,6 @@ public interface Rule {
*/ */
String name(); String name();
/**
* Get a random entry.
*
* @param config the configuration
* @param level the call level
* @return the entry
*/
String random(Bnf config, int level);
/**
* Get the last entry.
*
* @return the last entry
*/
Rule last();
/** /**
* Update cross references. * Update cross references.
* *
...@@ -63,12 +47,10 @@ public interface Rule { ...@@ -63,12 +47,10 @@ public interface Rule {
boolean matchRemove(Sentence sentence); boolean matchRemove(Sentence sentence);
/** /**
* Get the HTML railroad. * Call the visit method in the given visitor.
* *
* @param config the configuration * @param visitor the visitor
* @param topLevel true if line break are permitted
* @return the railroad
*/ */
String getHtmlRailroad(Bnf config, boolean topLevel); void accept(BnfVisitor visitor);
} }
...@@ -33,34 +33,14 @@ public class RuleElement implements Rule { ...@@ -33,34 +33,14 @@ public class RuleElement implements Rule {
return name; return name;
} }
public String getHtmlRailroad(Bnf config, boolean topLevel) { public void accept(BnfVisitor visitor) {
String x; visitor.visitRuleElement(keyword, name, link);
if (keyword) {
x = StringUtils.xmlText(name.trim());
} else {
x = config.getLink(name.trim());
}
return "<code class=\"c\">" + x + "</code>";
}
public String random(Bnf config, int level) {
if (keyword) {
return name.length() > 1 ? " " + name + " " : name;
}
if (link != null) {
return link.random(config, level + 1);
}
throw new AssertionError(name);
} }
public String name() { public String name() {
return name; return name;
} }
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) { public void setLinks(HashMap<String, RuleHead> ruleMap) {
if (link != null) { if (link != null) {
link.setLinks(ruleMap); link.setLinks(ruleMap);
......
...@@ -7,21 +7,20 @@ ...@@ -7,21 +7,20 @@
package org.h2.bnf; package org.h2.bnf;
import java.util.HashMap; import java.util.HashMap;
import java.util.Random;
/** /**
* Represents a hard coded terminal rule in a BNF object. * Represents a hard coded terminal rule in a BNF object.
*/ */
public class RuleFixed implements Rule { public class RuleFixed implements Rule {
static final int YMD = 0, HMS = 1, NANOS = 2; public static final int YMD = 0, HMS = 1, NANOS = 2;
static final int ANY_EXCEPT_SINGLE_QUOTE = 3; public static final int ANY_EXCEPT_SINGLE_QUOTE = 3;
static final int ANY_EXCEPT_DOUBLE_QUOTE = 4; public static final int ANY_EXCEPT_DOUBLE_QUOTE = 4;
static final int ANY_UNTIL_EOL = 5; public static final int ANY_UNTIL_EOL = 5;
static final int ANY_UNTIL_END = 6; public static final int ANY_UNTIL_END = 6;
static final int ANY_WORD = 7; public static final int ANY_WORD = 7;
static final int ANY_EXCEPT_2_DOLLAR = 8; public static final int ANY_EXCEPT_2_DOLLAR = 8;
static final int HEX_START = 10, CONCAT = 11, AZ_UNDERSCORE = 12, AF = 13, DIGIT = 14; public static final int HEX_START = 10, CONCAT = 11, AZ_UNDERSCORE = 12, AF = 13, DIGIT = 14;
static final int OPEN_BRACKET = 15, CLOSE_BRACKET = 16; public static final int OPEN_BRACKET = 15, CLOSE_BRACKET = 16;
private final int type; private final int type;
...@@ -64,94 +63,14 @@ public class RuleFixed implements Rule { ...@@ -64,94 +63,14 @@ public class RuleFixed implements Rule {
} }
} }
public String getHtmlRailroad(Bnf config, boolean topLevel) { public void accept(BnfVisitor visitor) {
return getHtmlText(); visitor.visitRuleFixed(type);
}
public String getHtmlText() {
switch(type) {
case YMD:
return "2000-01-01";
case HMS:
return "12:00:00";
case NANOS:
return "000000000";
case ANY_UNTIL_EOL:
case ANY_EXCEPT_SINGLE_QUOTE:
case ANY_EXCEPT_DOUBLE_QUOTE:
case ANY_WORD:
case ANY_EXCEPT_2_DOLLAR:
case ANY_UNTIL_END: {
return "anything";
}
case HEX_START:
return "0x";
case CONCAT:
return "||";
case AZ_UNDERSCORE:
return "A-Z | _";
case AF:
return "A-F";
case DIGIT:
return "0-9";
case OPEN_BRACKET:
return "[";
case CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
}
public String random(Bnf config, int level) {
Random r = config.getRandom();
switch (type) {
case YMD:
return (1800 + r.nextInt(200)) + "-" + (1 + r.nextInt(12)) + "-" + (1 + r.nextInt(31));
case HMS:
return (r.nextInt(24)) + "-" + (r.nextInt(60)) + "-" + (r.nextInt(60));
case NANOS:
return "" + (r.nextInt(100000) + r.nextInt(10000));
case ANY_UNTIL_EOL:
case ANY_EXCEPT_SINGLE_QUOTE:
case ANY_EXCEPT_DOUBLE_QUOTE:
case ANY_WORD:
case ANY_EXCEPT_2_DOLLAR:
case ANY_UNTIL_END: {
StringBuilder buff = new StringBuilder();
int len = r.nextBoolean() ? 1 : r.nextInt(5);
for (int i = 0; i < len; i++) {
buff.append((char) ('A' + r.nextInt('C' - 'A')));
}
return buff.toString();
}
case HEX_START:
return "0x";
case CONCAT:
return "||";
case AZ_UNDERSCORE:
return "" + (char) ('A' + r.nextInt('C' - 'A'));
case AF:
return "" + (char) ('A' + r.nextInt('F' - 'A'));
case DIGIT:
return "" + (char) ('0' + r.nextInt(10));
case OPEN_BRACKET:
return "[";
case CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
} }
public String name() { public String name() {
return "type="+type; return "type="+type;
} }
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) { public void setLinks(HashMap<String, RuleHead> ruleMap) {
// nothing to do // nothing to do
} }
......
...@@ -53,71 +53,14 @@ public class RuleList implements Rule { ...@@ -53,71 +53,14 @@ public class RuleList implements Rule {
return buff.toString(); return buff.toString();
} }
public String getHtmlRailroad(Bnf config, boolean topLevel) { public void accept(BnfVisitor visitor) {
StringBuilder buff = new StringBuilder(); visitor.visitRuleList(or, list);
if (or) {
buff.append("<table class=\"railroad\">");
int i = 0;
for (Rule r : list) {
String a = i == 0 ? "t" : i == list.size() - 1 ? "l" : "k";
i++;
buff.append("<tr class=\"railroad\"><td class=\"" + a + "s\"></td><td class=\"d\">");
buff.append(r.getHtmlRailroad(config, false));
buff.append("</td><td class=\"" + a + "e\"></td></tr>");
}
buff.append("</table>");
} else {
if (!topLevel) {
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\">");
}
for (Rule r : list) {
if (!topLevel) {
buff.append("<td class=\"d\">");
}
buff.append(r.getHtmlRailroad(config, false));
if (!topLevel) {
buff.append("</td>");
}
}
if (!topLevel) {
buff.append("</tr></table>");
}
}
return buff.toString();
}
public String random(Bnf config, int level) {
if (or) {
if (level > 10) {
if (level > 1000) {
// better than stack overflow
throw new AssertionError();
}
return get(0).random(config, level);
}
int idx = config.getRandom().nextInt(list.size());
return get(idx).random(config, level + 1);
}
StringBuilder buff = new StringBuilder();
for (Rule r : list) {
buff.append(r.random(config, level+1));
}
return buff.toString();
}
private Rule get(int idx) {
return list.get(idx);
} }
public String name() { public String name() {
return null; return null;
} }
public Rule last() {
return get(list.size() - 1);
}
public void setLinks(HashMap<String, RuleHead> ruleMap) { public void setLinks(HashMap<String, RuleHead> ruleMap) {
if (!mapSet) { if (!mapSet) {
for (Rule r : list) { for (Rule r : list) {
......
...@@ -23,31 +23,14 @@ public class RuleOptional implements Rule { ...@@ -23,31 +23,14 @@ public class RuleOptional implements Rule {
return "[" + rule.toString() + "]"; return "[" + rule.toString() + "]";
} }
public String getHtmlRailroad(Bnf config, boolean topLevel) { public void accept(BnfVisitor visitor) {
StringBuilder buff = new StringBuilder(); visitor.visitRuleOptional(rule);
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\"><td class=\"ts\"></td><td class=\"d\">&nbsp;</td><td class=\"te\"></td></tr>");
buff.append("<tr class=\"railroad\"><td class=\"ls\"></td><td class=\"d\">");
buff.append(rule.getHtmlRailroad(config, false));
buff.append("</td><td class=\"le\"></td></tr></table>");
return buff.toString();
} }
public String name() { public String name() {
return null; return null;
} }
public String random(Bnf config, int level) {
if (level > 10 ? config.getRandom().nextInt(level) == 1 : config.getRandom().nextInt(4) == 1) {
return rule.random(config, level + 1);
}
return "";
}
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) { public void setLinks(HashMap<String, RuleHead> ruleMap) {
if (!mapSet) { if (!mapSet) {
rule.setLinks(ruleMap); rule.setLinks(ruleMap);
......
...@@ -13,8 +13,6 @@ import java.util.HashMap; ...@@ -13,8 +13,6 @@ import java.util.HashMap;
*/ */
public class RuleRepeat implements Rule { public class RuleRepeat implements Rule {
private static final boolean RAILROAD_DOTS = true;
private final Rule rule; private final Rule rule;
private final boolean comma; private final boolean comma;
...@@ -27,43 +25,18 @@ public class RuleRepeat implements Rule { ...@@ -27,43 +25,18 @@ public class RuleRepeat implements Rule {
return "..."; return "...";
} }
public String getHtmlRailroad(Bnf config, boolean topLevel) { public void accept(BnfVisitor visitor) {
StringBuilder buff = new StringBuilder(); visitor.visitRuleRepeat(comma, rule);
if (RAILROAD_DOTS) {
buff.append("<code class=\"c\">");
if (comma) {
buff.append(", ");
}
buff.append("...</code>");
} else {
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\"><td class=\"te\"></td>");
buff.append("<td class=\"d\">");
buff.append(rule.getHtmlRailroad(config, false));
buff.append("</td><td class=\"ts\"></td></tr>");
buff.append("<tr class=\"railroad\"><td class=\"ls\"></td>");
buff.append("<td class=\"d\">&nbsp;</td>");
buff.append("<td class=\"le\"></td></tr></table>");
}
return buff.toString();
} }
public String name() { public String name() {
return rule.name(); return rule.name();
} }
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) { public void setLinks(HashMap<String, RuleHead> ruleMap) {
// rule.setLinks(ruleMap); // rule.setLinks(ruleMap);
} }
public String random(Bnf config, int level) {
return rule.random(config, level);
}
public boolean matchRemove(Sentence sentence) { public boolean matchRemove(Sentence sentence) {
if (sentence.shouldStop()) { if (sentence.shouldStop()) {
return false; return false;
......
...@@ -9,8 +9,7 @@ package org.h2.server.web; ...@@ -9,8 +9,7 @@ package org.h2.server.web;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Bnf;
import org.h2.bnf.Rule; import org.h2.bnf.Rule;
import org.h2.bnf.RuleHead; import org.h2.bnf.RuleHead;
import org.h2.bnf.Sentence; import org.h2.bnf.Sentence;
...@@ -61,14 +60,6 @@ public class DbContextRule implements Rule { ...@@ -61,14 +60,6 @@ public class DbContextRule implements Rule {
return null; return null;
} }
public String random(Bnf config, int level) {
return null;
}
public Rule last() {
return this;
}
public void setLinks(HashMap<String, RuleHead> ruleMap) { public void setLinks(HashMap<String, RuleHead> ruleMap) {
// nothing to do // nothing to do
} }
...@@ -477,8 +468,8 @@ public class DbContextRule implements Rule { ...@@ -477,8 +468,8 @@ public class DbContextRule implements Rule {
return best; return best;
} }
public String getHtmlRailroad(Bnf config, boolean topLevel) { public void accept(BnfVisitor visitor) {
return null; // nothing to do
} }
} }
...@@ -288,6 +288,7 @@ java org.h2.test.TestAll timer ...@@ -288,6 +288,7 @@ java org.h2.test.TestAll timer
/* /*
Row.getMemorySize Row.getMemorySize
FileStore.sync, Database.sync() (CHECKPOINT SYNC) FileStore.sync, Database.sync() (CHECKPOINT SYNC)
H2 Console: use reflection for @INFO; maybe compiler for loop
document in performance section: document in performance section:
PreparedStatement prep = conn.prepareStatement( PreparedStatement prep = conn.prepareStatement(
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.util.ArrayList;
import java.util.Random;
import org.h2.bnf.Bnf;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule;
import org.h2.bnf.RuleFixed;
import org.h2.bnf.RuleHead;
import org.h2.util.New;
/**
* A BNF visitor that generates a random SQL statement.
*/
public class BnfRandom implements BnfVisitor {
private static final boolean SHOW_SYNTAX = false;
private final Bnf config;
private final Random random = new Random();
private final ArrayList<RuleHead> statements = New.arrayList();
private int level;
private String sql;
BnfRandom() throws Exception {
config = Bnf.getInstance(null);
config.linkStatements();
ArrayList<RuleHead> all = config.getStatements();
// go backwards so we can append at the end
for (int i = all.size() - 1; i >= 0; i--) {
RuleHead r = all.get(i);
String topic = r.getTopic().toLowerCase();
int weight = 0;
if (topic.equals("select")) {
weight = 10;
} else if (topic.equals("create table")) {
weight = 20;
} else if (topic.equals("insert")) {
weight = 5;
} else if (topic.startsWith("update")) {
weight = 3;
} else if (topic.startsWith("delete")) {
weight = 3;
} else if (topic.startsWith("drop")) {
weight = 2;
}
if (SHOW_SYNTAX) {
System.out.println(r.getTopic());
}
for (int j = 0; j < weight; j++) {
statements.add(r);
}
}
}
public String getRandomSQL() {
int sid = random.nextInt(statements.size());
RuleHead r = statements.get(sid);
level = 0;
r.getRule().accept(this);
sql = sql.trim();
if (sql.length() > 0) {
if (sql.indexOf("TRACE_LEVEL_") < 0 && sql.indexOf("COLLATION") < 0
&& sql.indexOf("SCRIPT ") < 0 && sql.indexOf("CSVWRITE") < 0
&& sql.indexOf("BACKUP") < 0 && sql.indexOf("DB_CLOSE_DELAY") < 0) {
if (SHOW_SYNTAX) {
System.out.println(" " + sql);
}
return sql;
}
}
return null;
}
public void visitRuleElement(boolean keyword, String name, Rule link) {
if (keyword) {
sql = name.length() > 1 ? " " + name + " " : name;
} else if (link != null) {
level++;
link.accept(this);
level--;
} else {
throw new AssertionError(name);
}
}
public void visitRuleFixed(int type) {
sql = getRandomFixed(type);
}
private String getRandomFixed(int type) {
Random r = random;
switch (type) {
case RuleFixed.YMD:
return (1800 + r.nextInt(200)) + "-" + (1 + r.nextInt(12)) + "-" + (1 + r.nextInt(31));
case RuleFixed.HMS:
return (r.nextInt(24)) + "-" + (r.nextInt(60)) + "-" + (r.nextInt(60));
case RuleFixed.NANOS:
return "" + (r.nextInt(100000) + r.nextInt(10000));
case RuleFixed.ANY_UNTIL_EOL:
case RuleFixed.ANY_EXCEPT_SINGLE_QUOTE:
case RuleFixed.ANY_EXCEPT_DOUBLE_QUOTE:
case RuleFixed.ANY_WORD:
case RuleFixed.ANY_EXCEPT_2_DOLLAR:
case RuleFixed.ANY_UNTIL_END: {
StringBuilder buff = new StringBuilder();
int len = r.nextBoolean() ? 1 : r.nextInt(5);
for (int i = 0; i < len; i++) {
buff.append((char) ('A' + r.nextInt('C' - 'A')));
}
return buff.toString();
}
case RuleFixed.HEX_START:
return "0x";
case RuleFixed.CONCAT:
return "||";
case RuleFixed.AZ_UNDERSCORE:
return "" + (char) ('A' + r.nextInt('C' - 'A'));
case RuleFixed.AF:
return "" + (char) ('A' + r.nextInt('F' - 'A'));
case RuleFixed.DIGIT:
return "" + (char) ('0' + r.nextInt(10));
case RuleFixed.OPEN_BRACKET:
return "[";
case RuleFixed.CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
}
public void visitRuleList(boolean or, ArrayList<Rule> list) {
if (or) {
if (level > 10) {
if (level > 1000) {
// better than stack overflow
throw new AssertionError();
}
list.get(0).accept(this);
return;
}
int idx = random.nextInt(list.size());
level++;
list.get(idx).accept(this);
level--;
return;
}
StringBuilder buff = new StringBuilder();
level++;
for (Rule r : list) {
r.accept(this);
buff.append(sql);
}
level--;
sql = buff.toString();
}
public void visitRuleOptional(Rule rule) {
if (level > 10 ? random.nextInt(level) == 1 : random.nextInt(4) == 1) {
level++;
rule.accept(this);
level--;
return;
}
sql = "";
}
public void visitRuleRepeat(boolean comma, Rule rule) {
rule.accept(this);
}
public void setSeed(int seed) {
random.setSeed(seed);
}
public int getStatementCount() {
return statements.size();
}
}
...@@ -9,10 +9,6 @@ package org.h2.test.synth; ...@@ -9,10 +9,6 @@ package org.h2.test.synth;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList;
import org.h2.bnf.Bnf;
import org.h2.bnf.RuleHead;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.store.fs.FileSystemMemory; import org.h2.store.fs.FileSystemMemory;
import org.h2.test.TestAll; import org.h2.test.TestAll;
...@@ -26,11 +22,9 @@ import org.h2.util.MathUtils; ...@@ -26,11 +22,9 @@ import org.h2.util.MathUtils;
public class TestRandomSQL extends TestBase { public class TestRandomSQL extends TestBase {
private int dbId; private int dbId;
private boolean showSQL;
private ArrayList<RuleHead> statements;
private int seed; private int seed;
private boolean exitOnError = true; private boolean exitOnError = true;
private Bnf bnfSyntax; private BnfRandom bnfRandom;
private int success, total; private int success, total;
/** /**
...@@ -99,40 +93,12 @@ public class TestRandomSQL extends TestBase { ...@@ -99,40 +93,12 @@ public class TestRandomSQL extends TestBase {
public TestBase init(TestAll conf) throws Exception { public TestBase init(TestAll conf) throws Exception {
super.init(conf); super.init(conf);
bnfSyntax = Bnf.getInstance(null); bnfRandom = new BnfRandom();
bnfSyntax.linkStatements();
statements = bnfSyntax.getStatements();
// go backwards so we can append at the end
for (int i = statements.size() - 1; i >= 0; i--) {
RuleHead r = statements.get(i);
String topic = r.getTopic();
int weight = 0;
if (topic.equals("select")) {
weight = 10;
} else if (topic.equals("createtable")) {
weight = 20;
} else if (topic.equals("insert")) {
weight = 5;
} else if (topic.startsWith("update")) {
weight = 3;
} else if (topic.startsWith("delete")) {
weight = 3;
} else if (topic.startsWith("drop")) {
weight = 2;
}
if (showSQL) {
System.out.println(r.getTopic());
}
for (int j = 0; j < weight; j++) {
statements.add(r);
}
}
return this; return this;
} }
private void testWithSeed(Bnf bnf) throws SQLException { private void testWithSeed() throws SQLException {
bnf.getRandom().setSeed(seed); bnfRandom.setSeed(seed);
Connection conn = null; Connection conn = null;
try { try {
conn = connect(); conn = connect();
...@@ -141,28 +107,20 @@ public class TestRandomSQL extends TestBase { ...@@ -141,28 +107,20 @@ public class TestRandomSQL extends TestBase {
conn = connect(); conn = connect();
} }
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
for (int i = 0; i < statements.size(); i++) {
int sid = bnf.getRandom().nextInt(statements.size()); for (int i = 0; i < bnfRandom.getStatementCount(); i++) {
RuleHead r = statements.get(sid); String sql = bnfRandom.getRandomSQL();
String rand = r.getRule().random(bnf, 0).trim(); if (sql != null) {
if (rand.length() > 0) {
try { try {
Thread.yield(); Thread.yield();
if (rand.indexOf("TRACE_LEVEL_") < 0 && rand.indexOf("COLLATION") < 0 total++;
&& rand.indexOf("SCRIPT ") < 0 && rand.indexOf("CSVWRITE") < 0 if (total % 100 == 0) {
&& rand.indexOf("BACKUP") < 0 && rand.indexOf("DB_CLOSE_DELAY") < 0) { printTime("total: " + total + " success: " + (100 * success / total) + "%");
if (showSQL) {
System.out.println(i + " " + rand);
}
total++;
if (total % 100 == 0) {
printTime("total: " + total + " success: " + (100 * success / total) + "%");
}
stat.execute(rand);
success++;
} }
stat.execute(sql);
success++;
} catch (SQLException e) { } catch (SQLException e) {
processException(rand, e); processException(sql, e);
} }
} }
} }
...@@ -184,7 +142,7 @@ public class TestRandomSQL extends TestBase { ...@@ -184,7 +142,7 @@ public class TestRandomSQL extends TestBase {
} catch (SQLException e) { } catch (SQLException e) {
processException("deleteDb", e); processException("deleteDb", e);
} }
testWithSeed(bnfSyntax); testWithSeed();
} finally { } finally {
System.setProperty(SysProperties.H2_SCRIPT_DIRECTORY, old); System.setProperty(SysProperties.H2_SCRIPT_DIRECTORY, old);
} }
...@@ -201,7 +159,6 @@ public class TestRandomSQL extends TestBase { ...@@ -201,7 +159,6 @@ public class TestRandomSQL extends TestBase {
} }
int len = getSize(2, 6); int len = getSize(2, 6);
exitOnError = false; exitOnError = false;
showSQL = false;
for (int a = 0; a < len; a++) { for (int a = 0; a < len; a++) {
int s = MathUtils.randomInt(Integer.MAX_VALUE); int s = MathUtils.randomInt(Integer.MAX_VALUE);
testCase(s); testCase(s);
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.build.doc;
import java.util.ArrayList;
import org.h2.bnf.Bnf;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule;
import org.h2.bnf.RuleFixed;
import org.h2.util.StringUtils;
/**
* A BNF visitor that generates HTML railroad diagrams.
*/
public class BnfRailroad implements BnfVisitor {
private static final boolean RAILROAD_DOTS = true;
private BnfSyntax syntaxVisitor;
private Bnf config;
private String html;
/**
* Generate the HTML for the given syntax.
*
* @param bnf the BNF parser
* @param syntaxLines the syntax
* @return the HTML
*/
public String getHtml(Bnf bnf, String syntaxLines) {
syntaxVisitor = new BnfSyntax();
this.config = bnf;
syntaxLines = StringUtils.replaceAll(syntaxLines, "\n ", " ");
String[] syntaxList = StringUtils.arraySplit(syntaxLines, '\n', true);
StringBuilder buff = new StringBuilder();
for (String s : syntaxList) {
bnf.visit(this, s);
html = StringUtils.replaceAll(html, "</code></td><td class=\"d\"><code class=\"c\">", " ");
if (buff.length() > 0) {
buff.append("<br />");
}
buff.append(html);
}
return buff.toString();
}
public void visitRuleElement(boolean keyword, String name, Rule link) {
String x;
if (keyword) {
x = StringUtils.xmlText(name.trim());
} else {
x = syntaxVisitor.getLink(config, name.trim());
}
html = "<code class=\"c\">" + x + "</code>";
}
public void visitRuleRepeat(boolean comma, Rule rule) {
StringBuilder buff = new StringBuilder();
if (RAILROAD_DOTS) {
buff.append("<code class=\"c\">");
if (comma) {
buff.append(", ");
}
buff.append("...</code>");
} else {
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\"><td class=\"te\"></td>");
buff.append("<td class=\"d\">");
rule.accept(this);
buff.append(html);
buff.append("</td><td class=\"ts\"></td></tr>");
buff.append("<tr class=\"railroad\"><td class=\"ls\"></td>");
buff.append("<td class=\"d\">&nbsp;</td>");
buff.append("<td class=\"le\"></td></tr></table>");
}
html = buff.toString();
}
public void visitRuleFixed(int type) {
html = getHtmlText(type);
}
static String getHtmlText(int type) {
switch(type) {
case RuleFixed.YMD:
return "2000-01-01";
case RuleFixed.HMS:
return "12:00:00";
case RuleFixed.NANOS:
return "000000000";
case RuleFixed.ANY_UNTIL_EOL:
case RuleFixed.ANY_EXCEPT_SINGLE_QUOTE:
case RuleFixed.ANY_EXCEPT_DOUBLE_QUOTE:
case RuleFixed.ANY_WORD:
case RuleFixed.ANY_EXCEPT_2_DOLLAR:
case RuleFixed.ANY_UNTIL_END: {
return "anything";
}
case RuleFixed.HEX_START:
return "0x";
case RuleFixed.CONCAT:
return "||";
case RuleFixed.AZ_UNDERSCORE:
return "A-Z | _";
case RuleFixed.AF:
return "A-F";
case RuleFixed.DIGIT:
return "0-9";
case RuleFixed.OPEN_BRACKET:
return "[";
case RuleFixed.CLOSE_BRACKET:
return "]";
default:
throw new AssertionError("type="+type);
}
}
public void visitRuleList(boolean or, ArrayList<Rule> list) {
StringBuilder buff = new StringBuilder();
if (or) {
buff.append("<table class=\"railroad\">");
int i = 0;
for (Rule r : list) {
String a = i == 0 ? "t" : i == list.size() - 1 ? "l" : "k";
i++;
buff.append("<tr class=\"railroad\"><td class=\"" + a + "s\"></td><td class=\"d\">");
r.accept(this);
buff.append(html);
buff.append("</td><td class=\"" + a + "e\"></td></tr>");
}
buff.append("</table>");
} else {
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\">");
for (Rule r : list) {
buff.append("<td class=\"d\">");
r.accept(this);
buff.append(html);
buff.append("</td>");
}
buff.append("</tr></table>");
}
html = buff.toString();
}
public void visitRuleOptional(Rule rule) {
StringBuilder buff = new StringBuilder();
buff.append("<table class=\"railroad\">");
buff.append("<tr class=\"railroad\"><td class=\"ts\"></td><td class=\"d\">&nbsp;</td><td class=\"te\"></td></tr>");
buff.append("<tr class=\"railroad\"><td class=\"ls\"></td><td class=\"d\">");
rule.accept(this);
buff.append(html);
buff.append("</td><td class=\"le\"></td></tr></table>");
html = buff.toString();
}
}
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.build.doc;
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.h2.bnf.Bnf;
import org.h2.bnf.BnfVisitor;
import org.h2.bnf.Rule;
import org.h2.bnf.RuleFixed;
import org.h2.bnf.RuleHead;
import org.h2.util.StringUtils;
/**
* A BNF visitor that generates BNF in HTML form.
*/
public class BnfSyntax implements BnfVisitor {
private String html;
public String getHtml(Bnf bnf, String syntaxLines) {
syntaxLines = StringUtils.replaceAll(syntaxLines, "\n ", "\n");
StringTokenizer tokenizer = bnf.getTokenizer(syntaxLines);
StringBuilder buff = new StringBuilder();
while (tokenizer.hasMoreTokens()) {
String s = tokenizer.nextToken();
if (s.length() == 1 || StringUtils.toUpperEnglish(s).equals(s)) {
buff.append(StringUtils.xmlText(s));
continue;
}
buff.append(getLink(bnf, s));
}
String s = buff.toString();
// ensure it works within XHTML comments
s = StringUtils.replaceAll(s, "--", "&#45;-");
return s;
}
String getLink(Bnf bnf, String token) {
RuleHead found = null;
String key = Bnf.getRuleMapKey(token);
for (int i = 0; i < token.length(); i++) {
String test = StringUtils.toLowerEnglish(key.substring(i));
RuleHead r = bnf.getRuleHead(test);
if (r != null) {
found = r;
break;
}
}
if (found == null) {
return token;
}
String page = "grammar.html";
if (found.getSection().startsWith("Data Types")) {
page = "datatypes.html";
} else if (found.getSection().startsWith("Functions")) {
page = "functions.html";
} else if (token.equals("@func@")) {
return "<a href=\"functions.html\">Function</a>";
} else if (found.getRule() instanceof RuleFixed) {
found.getRule().accept(this);
return html;
}
String link = found.getTopic().toLowerCase().replace(' ', '_');
link = page + "#" + StringUtils.urlEncode(link);
return "<a href=\"" + link + "\">" + token + "</a>";
}
public void visitRuleElement(boolean keyword, String name, Rule link) {
// not used
}
public void visitRuleFixed(int type) {
html = BnfRailroad.getHtmlText(type);
}
public void visitRuleList(boolean or, ArrayList<Rule> list) {
// not used
}
public void visitRuleOptional(Rule rule) {
// not used
}
public void visitRuleRepeat(boolean comma, Rule rule) {
// not used
}
}
...@@ -137,11 +137,13 @@ public class GenerateDoc { ...@@ -137,11 +137,13 @@ public class GenerateDoc {
String topic = rs.getString("TOPIC"); String topic = rs.getString("TOPIC");
String syntax = rs.getString("SYNTAX").trim(); String syntax = rs.getString("SYNTAX").trim();
if (railroads) { if (railroads) {
String railroad = bnf.getRailroadHtml(syntax); BnfRailroad r = new BnfRailroad();
String railroad = r.getHtml(bnf, syntax);
map.put("railroad", railroad); map.put("railroad", railroad);
} }
syntax = bnf.getSyntaxHtml(syntax); BnfSyntax visitor = new BnfSyntax();
map.put("syntax", syntax); String syntaxHtml = visitor.getHtml(bnf, syntax);
map.put("syntax", syntaxHtml);
// remove newlines in the regular text // remove newlines in the regular text
String text = map.get("text"); String text = map.get("text");
......
...@@ -18,6 +18,8 @@ import java.util.HashMap; ...@@ -18,6 +18,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import org.h2.bnf.Bnf; import org.h2.bnf.Bnf;
import org.h2.build.BuildBase; import org.h2.build.BuildBase;
import org.h2.build.doc.BnfRailroad;
import org.h2.build.doc.BnfSyntax;
import org.h2.build.doc.RailroadImages; import org.h2.build.doc.RailroadImages;
import org.h2.server.web.PageParser; import org.h2.server.web.PageParser;
import org.h2.tools.Csv; import org.h2.tools.Csv;
...@@ -88,12 +90,13 @@ public class Railroads { ...@@ -88,12 +90,13 @@ public class Railroads {
String topic = rs.getString("TOPIC"); String topic = rs.getString("TOPIC");
String syntax = rs.getString("SYNTAX").trim(); String syntax = rs.getString("SYNTAX").trim();
if (railroads) { if (railroads) {
String railroad = bnf.getRailroadHtml(syntax); BnfRailroad r = new BnfRailroad();
String railroad = r.getHtml(bnf, syntax);
map.put("railroad", railroad); map.put("railroad", railroad);
} }
syntax = bnf.getSyntaxHtml(syntax); BnfSyntax visitor = new BnfSyntax();
map.put("syntax", syntax); String syntaxHtml = visitor.getHtml(bnf, syntax);
map.put("syntax", syntaxHtml);
// remove newlines in the regular text // remove newlines in the regular text
String text = map.get("text"); String text = map.get("text");
if (text != null) { if (text != null) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论