Mercurial > hg > Applications > JavaLite
changeset 75:75c0ecc1f09a order-fix
*** empty log message ***
author | kono |
---|---|
date | Thu, 17 Jan 2008 03:20:48 +0900 |
parents | 147864a924cd |
children | 854477cf9aa9 |
files | src/parser/Command.java src/parser/Dictionary.java src/parser/ExecutorInterface.java src/parser/LogicNodeFactoryInterface.java src/parser/LogicNodeParser.java src/parser/LogicNodeParserTest.java src/parser/LogicNodeScanner.java src/parser/LogicNodeScannerTest.java src/parser/LogicNodeScope.java src/parser/MacroNodeParser.java src/parser/MacroNodeParserInterface.java src/parser/NullExecutor.java src/parser/Token.java src/parser/TokenID.java |
diffstat | 14 files changed, 1231 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/Command.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,8 @@ +package parser; + +import java.util.LinkedList; + + +public interface Command<Node> { + Node exec(Node predicate, Node value, LinkedList<Node>args); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/Dictionary.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,26 @@ +package parser; + +import java.util.HashMap; + +public class Dictionary<Node> extends HashMap<String,Token<Node>> { + + private static final long serialVersionUID = 1L; + + public Token<Node> reserve(String name,TokenID type) { + Token<Node> t = new Token<Node>(name,type); + this.put(name, t); + return t; + } + + + public Token<Node> reserve(String name,TokenID type, int order) { + Token<Node> t = new Token<Node>(name,type,order); + this.put(name, t); + return t; + } + + + public Token<Node> reserve(String string) { + return reserve(string,TokenID.Any); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/ExecutorInterface.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,7 @@ +package parser; + +public interface ExecutorInterface<Node> { + + public void verify(Node n); + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/LogicNodeFactoryInterface.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,30 @@ +package parser; + +import java.util.LinkedList; + +public interface LogicNodeFactoryInterface<T> { + public T andNode(T left, T right) ; + + public T orNode(T left, T right) ; + + public T notNode(T node) ; + + public T bddNode(T cond, T high, T value); + + public T variableNode(String name,boolean value); + + public T existsNode(T var,T node); + + public T trueNode(); + + public T falseNode() ; + + public T predicateNode(T predicate, LinkedList<T> arg); + + public T numberNode(int num); + + public T macroNode(T macro, String body, int order, Command<T>command, + MacroNodeParserInterface<T> parser); + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/LogicNodeParser.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,275 @@ +package parser; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.LinkedList; + +import verifier.LogicSolver; + + +public class LogicNodeParser<Node extends LogicSolver> { + + /* + * Simple Logic Node Parser + * + * Syntax is based on Prolog implementation of LITE verifier, but + * it is not an operator order grammer parser. Currently we use + * object comparison, but it is better to use id, because we can + * use switch statements. + * + * Tokenize is done by Scanner. Nested parsing (necesssary for macro + * expansion) is done by scanner.push()/pop(). + * + * Scope is handled by association linked list based scope. Use scope.push()/pop() + * and scope.getLocalName(s). + * + * All nodes are created by logicnNodeFactory. In order to handle various + * logic, constructed Node is parameterized. + * + * 2007/12 Shinji Kono + */ + + public LogicNodeFactoryInterface<Node> logicNodeFactory; + public LogicNodeScanner<Node> scanner; + public Dictionary<Node> dict; + public LogicNodeScope<Node> scope; + public int position; + public int end; + public Token<Node> nextToken; + private ExecutorInterface<Node> exec = new NullExecutor<Node>(); + + public LogicNodeParser() { + super(); + initialize(); + } + + public LogicNodeParser(String string, LogicNodeFactoryInterface<Node> lf) { + super(); + logicNodeFactory = lf; + initialize(); + scanner.set(string); + } + + public void initReservedWord() { + dict.reserve(",",TokenID.And); + dict.reserve(";",TokenID.Or); + + dict.reserve("(",TokenID.Paren); + dict.reserve(")",TokenID.CloseParen); + dict.reserve("{",TokenID.CurParen); + dict.reserve("}",TokenID.CloseCurParen); + dict.reserve("[",TokenID.BParen); + dict.reserve("]",TokenID.CloseBParen); + dict.reserve("~",TokenID.Not); + dict.reserve(".",TokenID.Period); + dict.reserve("?",TokenID.Question); + dict.reserve(":",TokenID.Colon); + dict.reserve("exists",TokenID.Exists); + dict.reserve("true",TokenID.True); + dict.reserve("false",TokenID.False); + } + + public void initialize() { + dict = new Dictionary<Node>(); + scope = new LogicNodeScope<Node>(null,dict); + initReservedWord(); + scanner = new LogicNodeScanner<Node>(dict); + } + + public Node parse() { + if (scanner==null) return null; + nextToken(); + return expr1(); + } + + public Node parse(String exp) { + Node n; + scanner = scanner.pushScanner(exp); + n = parse(); + scanner = scanner.popScanner(); + nextToken = scanner.nextToken; + return n; + + } + + public void parseFile(String file) { + try { + scanner = scanner.pushScannerFile(file); + } catch (FileNotFoundException e) { + error("Can't open "+file); + return; + } + doParse(); + } + + public void parse(InputStream file) { + scanner = scanner.pushScannerFile(file); + doParse(); + } + + public Node doParse() { + Node n; + do { + n=parse(); + exec.verify(n); + } while(scanner.hasRemaining()); + scanner = scanner.popScanner(); + nextToken = scanner.nextToken; + return n; + } + + public Node expr1() { + Node n = expr2(); + while(nextToken.type == TokenID.Or) { + nextToken(); + n = logicNodeFactory.orNode(n, expr2()); + } + return n; + } + + public Node expr2() { + Node n = expr3(); + while(nextToken.type == TokenID.And) { + nextToken(); + n = logicNodeFactory.andNode(n, expr3()); + } + return n; + } + + public Node expr3() { + if (nextToken.type==TokenID.Not) { + nextToken(); + return logicNodeFactory.notNode(expr3()); + } else if (nextToken.type==TokenID.Exists) { + return quantifier(); + } + return expr4(); + } + + /* + * exists(x,(x,a)) + * this operator has a local scope + */ + public Node quantifier() { + LinkedList<Node> arg; + nextToken(); + if (nextToken.type==TokenID.Paren) { + // symbolc x havs been already converted to a variable when it was + // evaluated as a macro argument. We cannot make it local here. + // Do it in exists evaluation. + nextToken(); + arg = arguments(); + if (arg.size()!=2 /* || !arg.get(0).isVariable() */) { + error("bad quantifier"); + return logicNodeFactory.trueNode(); + } + return logicNodeFactory.existsNode(arg.get(0),arg.get(1)); + } else { + error("( expected"); + return logicNodeFactory.trueNode(); + } + } + + public Node expr4() { + Node n = term(); + if (nextToken.type==TokenID.Paren) { + // predicate ( or macro ) + nextToken(); + n = logicNodeFactory.predicateNode(n,arguments()); + } + return n; + } + + protected LinkedList<Node> arguments() { + Node n; + LinkedList<Node> arg = new LinkedList<Node>(); + while(nextToken.type!=TokenID.CloseParen) { + n = expr3(); + arg.add(n); + if (nextToken.type == TokenID.And) + nextToken(); + } + if (nextToken.type==TokenID.CloseParen) nextToken(); + else { + scanner.error(") expected"); + } + return arg; + } + + protected Node term() { + Node n = null; + switch (nextToken.type) { + case Paren: + nextToken(); + n = expr1(); + if (nextToken.type==TokenID.CloseParen) { + } else { // syntax error; + scanner.error(") expected but got "+nextToken); + return logicNodeFactory.trueNode(); + } + break; + case CurParen: + nextToken(); + n = expr1(); + if (nextToken.type==TokenID.CloseCurParen) { + } else { // syntax error; + scanner.error("} expected"); + } + break; + case NUMBER: + n = logicNodeFactory.numberNode(Integer.parseInt(nextToken.name)); + break; + case True: + n = logicNodeFactory.trueNode(); + break; + case False: + n = logicNodeFactory.falseNode(); + break; + case NULL: case Period: + break; + default: + if (nextToken.type.isVariable()) + n = makeVariable(nextToken); + else { + // error("Internal ununderstandable term '"+nextToken+"' type: "+nextToken.type+"."); + n = makeVariable(nextToken); // skip any + } + } + nextToken(); + return n; + } + /* + * All names in LogicNode are variables. A token represents + * name. To get the variable, use this method. + */ + protected Node makeVariable(Token<Node> t) { + Node n; + if ((n=t.value())==null) { + n = logicNodeFactory.variableNode(t.realName(),true); + t.setValue(n); + } + // n.token = t; + return n; + } + + /* + * Syntactical Short cut for scanner.textToken() + * + * It never returns null, to check EOF, + * nextToken.type==TokenID.NULL + */ + public Token<Node> nextToken() { + return nextToken = scanner.nextToken(); + } + + public void setExecutor(ExecutorInterface<Node> exec) { + this.exec = exec; + } + + public void error(String err) { + scanner.error(err); + } + + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/LogicNodeParserTest.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,53 @@ +package parser; + +import verifier.LogicSolver; +import verifier.LogicSolverFactory; + +public class LogicNodeParserTest { + + static MacroNodeParser<LogicSolver> parser; + + public static void main(String arg[]) { + LogicNodeParser<LogicSolver> p; + LogicSolver n; + LogicNodeFactoryInterface<LogicSolver> lf = new LogicSolverFactory(); + if (true) { + p = new LogicNodeParser<LogicSolver>("a,(b;~c)",lf); + n = p.parse(); + if (n!=null) System.out.print(n); System.out.println("."); + } + + p = new LogicNodeParser<LogicSolver>("exists(x,(a,x))",lf); + n = p.parse(); + if (n!=null) System.out.print(n); System.out.println("."); + + parser = (MacroNodeParser<LogicSolver>) p; + n = parser.parse(); + if (n!=null) System.out.print(n); System.out.println("."); + + sat("~exists(x,(x,b))"); + + parser.define("forall(x,body)", "~exists(x,(~body))"); + sat("forall(a,(a,~a))"); + + parser.define("P->Q","(not (P);Q))",300); // imply + sat("p->q"); + parser.define("not R","~R",50); + sat("p->q"); + + parser.define("P and Q", "(P,Q)",200); + + sat("p , q , r"); + sat("p and q and r"); + sat("p -> q and r"); + sat("p and q and r -> s"); + sat("p and q -> r -> s"); + sat("p -> q -> r and s"); + sat("p -> q and r and s"); + } + + public static void sat(String s) { + LogicSolver n = parser.parse(s); + if (n!=null) System.out.print(n); System.out.println("."); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/LogicNodeScanner.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,291 @@ +package parser; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.CharBuffer; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class LogicNodeScanner<Node> { + + /* + * Tokenizer for LogicNode Parser + * Pattern/Matcher implementation + * + * sannerStack is used for nested parsing. + * scanner.push(); + * parser.parse(exp); + * nextToken = scanner.pop(); + * + * 2007/12 Shinji Kono + */ + + public int lineno; + public Matcher scan; + public Token<Node> nextToken; + public Dictionary<Node> dict; + public LogicNodeScanner next; + protected CharBuffer cb; + private InputStreamReader file; + private String filename; + public LogicNodeScanner<Node> prev; + public Token<Node> nullToken ; + + public LogicNodeScanner(Dictionary<Node> dict) { + this.dict = dict; + nullToken = new Token<Node>("",TokenID.NULL,0); + } + + /* + * Scanner Container for Stack + */ + public LogicNodeScanner(LogicNodeScanner<Node> prev, Dictionary<Node> dict, Token<Node> nullToken) { + this.prev = prev; + this.dict = dict; + this.nullToken = nullToken; + } + + + // Pattern must contain exact 1 group + public static Pattern tokenPat = Pattern.compile( + "(\\&\\&|[*\\?\\:\\^.()+{},;~\\&@]|\\[\\]|\\:\\=|\\<\\=|\\<\\>|\\=\\>|\\=|" + + "\\<\\-\\>|\\-\\>|\\<\\-|\\[|\\])" + ); + public static Pattern namePat = Pattern.compile("([a-zA-Z]\\w*)"); + public static final Pattern numPat = Pattern.compile("([0-9]+)"); + public static final Pattern stringPat1 = Pattern.compile("\\\"([^\"]*)\\\""); + public static final Pattern stringPat = Pattern.compile("\\'([^\\']*)\\'"); + public static final Pattern commentPat = Pattern.compile("(%.*)"); + public static final Pattern errorPat = Pattern.compile("([^\\s])"); + public static final Pattern anyPat = Pattern.compile("(.)"); + private static final int BufferSize = 4096; + + /* + * Get next token + * + * No looking up method nor put back. It never returns null but + * may return nullToken. So nextToken.type is always valid. + * nullToken means the end of the input. + * + * Token is a syntax element and it may have macro binidng as + * predicate, infix or prefix operaotor. To get the value, use + * makeVariable(). Operator order for infix and prefix is in + * Token.order. TokenID.order is default order for fix element and + * currently never used. + * + * When matcher hit an end of the input, hasRemaining() method try + * to extend the input using extendInput(). + */ + + public Token<Node> nextToken() { + String s; + nextToken = nullToken; + while(hasRemaining()) { + scan.reset(); // to tell CharBuffer is modified + if ((s=next(tokenPat))!=null) { + Token<Node> t; + if ((t = dict.get(s))==null) { + dict.put(s, t = new Token<Node>(s,TokenID.Any)); + } + return nextToken = t; + } else if ((s=next(stringPat))!=null||(s=next(namePat))!=null) { + Token<Node> t; + if ((t = dict.get(s))==null) { + dict.put(s, t = new Token<Node>(s,TokenID.VARIABLE)); + } + return nextToken = t; + } else if ((s=next(numPat))!=null) { + return nextToken = new Token<Node>(s,TokenID.NUMBER); + } else if ((s=next(stringPat1))!=null) { + return nextToken = new Token<Node>(s,TokenID.STRING); + } else if ((s=next(commentPat))!=null) { + while(cb.hasRemaining()&&next(anyPat)!=null); // skip until eol (in case of buffer full) + continue; + } else if ((s=next(errorPat))!=null) { + error("Don't understand '"+s+"'"); + continue; + } else if ((s=next(anyPat))!=null) { + // skip space + continue; + } else { + lineno++; + cb.get(); // discard one ( new line ) + } + } + return nextToken; + } + + protected String next(Pattern pattern) { + String s = null; + while(true) { + Boolean match = scan.usePattern(pattern).lookingAt(); + if (scan.hitEnd()) { + if (extendInput()) { + // input is extended try again + scan.reset(); + continue; + } + // no extension. + } + if (match) { + s = scan.group(1); + // fix position in CharBuffer + // scan.end() is relative position + cb.position(cb.position()+scan.end()); + // scan.reset(); will be done on top of nextToken() + } + if (scan.hitEnd()) { + // previous extendInput is failed because of Buffer full. + // Now we have a space. Try again + extendInput();scan.hitEnd(); + } + return s; + } + } + + public boolean hasRemaining() { + return cb.hasRemaining()||extendInput(); + } + + /* + * Extend Input data + */ + protected boolean extendInput() { + if (file!=null && cb.position()!=0) { + // move remaining data to the top, set position for next read + cb.compact(); + try { + if (file.read(cb)>0) { + cb.flip(); // prepare for get (but we don't...) + return true; + } else { + throw new IOException(); + } + } catch (IOException e) { + file = null ; + cb.flip(); + } + } + return false; + } + + protected LogicNodeScanner<Node> pushScanner(String exp) { + // Save current matcher for nested parsing + return new LogicNodeScanner<Node>(this,dict,nullToken).set(exp); + } + + protected LogicNodeScanner<Node> pushScannerFile(String newfile) throws FileNotFoundException { + // Save current matcher for nested file + return new LogicNodeScanner<Node>(this,dict,nullToken).setFile(newfile); + } + + public LogicNodeScanner<Node> pushScannerFile(InputStream newfile) { + return new LogicNodeScanner<Node>(this,dict,nullToken).setFile(newfile); + } + + protected LogicNodeScanner<Node> popScanner() { + return prev; + } + + private LogicNodeScanner<Node> findFileName() { + for(LogicNodeScanner<Node> s = this;s!=null ; s = s.prev) { + if (s.filename!=null) return s; + } + return null; + } + /* + * Read From String + */ + + public LogicNodeScanner<Node> set(String exp) { + cb = CharBuffer.wrap(exp); + scan = tokenPat.matcher(cb); + filename = null; file = null; + nextToken = nullToken; + return this; + } + + /* + * Read From File + * We cannot read symbol bigger than Buffersize + */ + public LogicNodeScanner<Node> setFile(String file) throws FileNotFoundException { + this.filename = file; + nextToken = nullToken; + set(new FileReader(file)); + return this; + } + + public LogicNodeScanner<Node> set(InputStreamReader file) { + this.file = file; + cb = CharBuffer.allocate(BufferSize); + try { + if (file.read(cb) <= 0) { + throw new IOException(); + } + } catch (IOException e) { + file = null; cb = null; + set(""); + return this; + } finally { + cb.flip(); + } + scan = tokenPat.matcher(cb); + lineno = 0; + return this; + } + + public void error(String err) { + LogicNodeScanner<Node> s = findFileName(); + if (s!=null) { + System.err.print(s.filename+":"+s.lineno+": "); + } + System.err.println("error: "+err); + } + + /* + * Iterator for Test Routing + * for(Token<Node> t: scanner.scanToken(FileReader(file)) { ... } + */ + + public Iterable<Token<Node>> scanToken(String exp) { + set(exp); + return iterator(); + } + + public Iterable<Token<Node>> scanToken(FileReader file) { + set(file); + return iterator(); + } + + private Iterable<Token<Node>> iterator() { + return new Iterable<Token<Node>>() { + public Iterator<Token<Node>> iterator() { + return new Iterator<Token<Node>>() { + public boolean hasNext() { + return hasRemaining(); + } + public Token<Node> next() { + return nextToken(); + } + public void remove() { + } + }; + } + }; + } + + private LogicNodeScanner<Node> setFile(InputStream newfile) { + this.filename = newfile.toString(); + nextToken = nullToken; + set(new InputStreamReader(newfile)); + return this; + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/LogicNodeScannerTest.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,48 @@ +package parser; + +import java.io.FileNotFoundException; +import java.io.FileReader; + +import verifier.LogicSolver; + +public class LogicNodeScannerTest { + + public static LogicNodeScanner<LogicSolver> scan; + + public static void main(String arg[]) { + initScanner(); + if (arg.length==0) { + arg = new String[1]; + arg[0] = "test"; + } + for(String file: arg) { + try { + scan(new FileReader(file)); + } catch (FileNotFoundException e) { + scan(file); + } + } + } + + public static void initScanner() { + Dictionary<LogicSolver> dict = new Dictionary<LogicSolver>(); + scan = new LogicNodeScanner<LogicSolver>(dict); + } + + + public static void scan(String exp) { + for(Token<LogicSolver> t : scan.scanToken(exp)) { + System.out.print(t+" "); + } + System.out.println(); + } + + public static void scan(FileReader file) { + for(Token<LogicSolver> t : scan.scanToken(file)) { + System.out.print(t+" "); + System.out.println(); + } + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/LogicNodeScope.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,48 @@ +package parser; + +import java.util.TreeMap; + +/* + * Scope mechnism for local variable + * exists(x,(x,y)) + * previous x token is stored in assocation list + * pop() remove local x token and restore previous x + * previous x may be null. + */ +public class LogicNodeScope<Node> { + public TreeMap<String,Token<Node>> scope; + public Dictionary<Node> dict; + public LogicNodeScope<Node> prev; + + + public LogicNodeScope(LogicNodeScope<Node>prev, Dictionary<Node> dict) { + this.dict = dict; + this.prev = prev; + this.scope = new TreeMap<String,Token<Node>>(); + } + + + // enter the scope + public LogicNodeScope<Node> push() { + return new LogicNodeScope<Node>(this,dict); + } + + // exit the scope + public LogicNodeScope<Node> pop() { + // restore local variable name + for(String name: scope.keySet()) { + Token<Node> t = scope.get(name); + dict.put(name, t); // overwrite + } + return prev; + } + + // make new local name in this scope + public Token<Node> getLocalName(String name) { + Token<Node> n = new Token<Node>(name); + Token<Node> t=dict.get(name); + scope.put(name, t); // remember original + dict.put(name,n); // overwrite entry with new one + return n; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/MacroNodeParser.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,291 @@ +package parser; + +import java.util.LinkedList; + +import verifier.LogicSolver; + +public class MacroNodeParser<Node extends LogicSolver> + extends LogicNodeParser<Node> + implements MacroNodeParserInterface<Node> { + + /* + * Logic Node Parser with Macro Expansion + * parser.define("head(var)","body = var"); + * "head(x+1)" is replcaed by "body = (x+1)" and parsed + * + * A macro definition is stored in MacroNode. The MacroNode is stored in + * dict by MacroNodeFactory.predicateNode(); MacroNode has to know the parser + * to parse expanded macro. It is possible to parse macro body before expansion, + * but its implementation is sligtly complex. + */ + + public LogicNodeFactoryInterface<Node> logicNodeFactory; + + public MacroNodeParser() { + super(); + } + + public MacroNodeParser(String str, LogicNodeFactoryInterface<Node> lf) { + this(); + logicNodeFactory = lf; + super.logicNodeFactory = logicNodeFactory; + initialize(); + scanner = scanner.pushScanner(str); + } + + public MacroNodeParser(LogicNodeFactoryInterface<Node> lf) { + this(); + logicNodeFactory = lf; + super.logicNodeFactory = logicNodeFactory; + initialize(); + } + + @Override + public Node expr2() { + Node n = exprM1(); + while(nextToken.type == TokenID.And) { + nextToken(); + n = logicNodeFactory.andNode(n, exprM1()); + } + return n; + } + + /* + * Recurseive Decent with operator order + * n1 op n2 op2 ... + * + * When the nextToken is a macro, nextToken.type is + * TokenID.VARIABLE, that is nextToken.type.order==1. + * MacroNode constructor assign the order in Token.order. + * Don't use Token.type.order. + */ + public Node exprM1() { + return exprM2(expr3()); + } + + public Node exprM2(Node n1) { + while (nextToken.isInfix()) { + Node op = makeVariable(nextToken); + LinkedList<Node> arg = new LinkedList<Node>(); + arg.add(n1); + nextToken(); + Node n2 = expr3(); + if (!nextToken.isInfix()) { + arg.add(n2); + return logicNodeFactory.predicateNode(op, arg); + } + if (op.order() > nextToken.order) { // a + b * + arg.add(exprM2(n2)); + return logicNodeFactory.predicateNode(op, arg); + } else { // a * b + + arg.add(n2); + n1 = logicNodeFactory.predicateNode(op, arg); + } + } + return n1; + } + + + @Override + public Node expr3() { + if (nextToken.type==TokenID.Not) { + nextToken(); + return logicNodeFactory.notNode(expr3()); + } else if (nextToken.isPrefix()) { + Node op = makeVariable(nextToken); + LinkedList<Node> arg = new LinkedList<Node>(); + nextToken(); + arg.add(expr3()); + return logicNodeFactory.predicateNode(op, arg ); + } else if (nextToken.type==TokenID.Exists) { + return quantifier(); + } + return expr4(); + } + + @Override + public Node expr4() { + if (nextToken.syntax==TokenID.ALIAS) { + LinkedList<Node> arg = new LinkedList<Node>(); // empty + Node op = makeVariable(nextToken); + nextToken(); + return logicNodeFactory.predicateNode(op, arg ); + } + Node n = term(); + if (nextToken.type==TokenID.Paren) { + // predicate ( or macro ) + nextToken(); + n = logicNodeFactory.predicateNode(n,arguments()); + } else if (nextToken.syntax==TokenID.ALIAS) { + LinkedList<Node> arg = new LinkedList<Node>(); // empty + Node op = makeVariable(nextToken); + nextToken(); + return logicNodeFactory.predicateNode(op, arg ); + } + return n; + } + + @Override + protected LinkedList<Node> arguments() { + Node n; + LinkedList<Node> arg = new LinkedList<Node>(); + while(nextToken.type!=TokenID.CloseParen) { + n = exprM1(); + arg.add(n); + if (nextToken.type == TokenID.And) + nextToken(); + } + if (nextToken.type==TokenID.CloseParen) nextToken(); + else { + scanner.error(") expected"); + } + return arg; + } + + /* + * + * Expand macro body with args. Called from PredicateNode Factory (if it is a macro) + */ + + public Node eval(LinkedList<? extends Node> args, Node predicate, String body, + LinkedList<? extends Node> vars) { + Node result; + int i = 0; + + if (vars==null) { + // TermMacro case + return parse(body); + } + // prepare new scope + scope = scope.push(); + for(Node var: vars) { + Node value; + if (i<args.size()) { + value = args.get(i++); + } else { + value = logicNodeFactory.trueNode(); + scanner.error("Macro "+predicate+" Argument mismatch"); + } + // make new token in the dictionary + Token<Node> local = scope.getLocalName(var.toString()); + // assign new value + local.setMacro(value); + } + // parse it in new bindings + result = parse(body); + scope = scope.pop(); + + return result; + } + + /* + * Make macro definition (store in the token in the dictionary) + */ + public void define(String macro, String body) { + define(macro, body, 0, null); + } + + public void define(String macro, String body, int order) { + define(macro, body, order, null); + } + + /* macro with command */ + public void define(String macro, String body, int order, Command<Node> command) { + Node m = parseMarcoHead(macro,order); + Node n = logicNodeFactory.macroNode(m, body, order, command, this); + + Token<Node> t; + LogicSolver p = m.predicate(); + if (p!=null && (t = dict.get(p.toString()))!=null) { + t.setMacro(n); + } else { + scanner.error("Internal error in define(\""+macro+"\",\""+n+"\")"); + } + } + + /* + * Macro Head parser + */ + private Node parseMarcoHead(String exp, int order) { + Node n; + scanner = scanner.pushScanner(exp); + nextToken(); + Token<Node> n1 = scanner.nextToken; + n = term(); + + if (nextToken.type==TokenID.Paren) { + nextToken(); + n = logicNodeFactory.predicateNode(n,parameter()); + if (n.arguments().size()==1) { + // all one argument predicates are prefix + n1.setPrefix(); + } + } else if (nextToken.type==TokenID.NULL) { + // No parameter + n = logicNodeFactory.predicateNode(n,null); + n1.syntax = TokenID.ALIAS; + } else { + Token<Node> opToken = nextToken; + Node op; + nextToken(); + Token<Node> n2 = nextToken; + LinkedList<Node> arg = new LinkedList<Node>(); + scope = scope.push(); + if (nextToken.type!=TokenID.NULL) { + // Assuming infix operator + opToken.setInfix(); + opToken.order = order; + op = makeVariable(opToken); + oneParameter(arg,n1); + oneParameter(arg,n2); + } else { + op = makeVariable(n1); + n1.setPrefix(); + n1.order = order; + oneParameter(arg,opToken); + } + scope = scope.pop(); + n = logicNodeFactory.predicateNode(op,arg); + } + + scanner = scanner.popScanner(); + nextToken = scanner.nextToken; + return n; + } + + /* + * Function parameter + * parameters are defined in local scope + */ + private LinkedList<Node> parameter() { + LinkedList<Node> arg = new LinkedList<Node>(); + + scope = scope.push(); + while(nextToken.type!=TokenID.CloseParen) { + if (nextToken.type==TokenID.NULL) break; + oneParameter(arg,nextToken); + nextToken(); + if (nextToken.type == TokenID.And) + nextToken(); + } + if (nextToken.type==TokenID.CloseParen) nextToken(); + else { + scanner.error(") expected"); + } + scope = scope.pop(); + return arg; + } + + public void oneParameter(LinkedList<Node> arg, Token<Node> nextToken) { + Token<Node> t; + Node v; + // If next token is a macro, parameter name is going to be replaced. + // We have to know the real symbol name. + + // more check is necessary + t = scope.getLocalName(nextToken.realName()); + t.setVariable(true); + v = makeVariable(t); + arg.add(v); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/MacroNodeParserInterface.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,11 @@ +package parser; + +import java.util.LinkedList; + +public interface MacroNodeParserInterface<Node> { + + public Node eval(LinkedList<? extends Node> args, Node predicate, String body, + LinkedList<? extends Node> name) ; + + public void define(String head,String body); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/NullExecutor.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,8 @@ +package parser; + +public class NullExecutor<Node> implements ExecutorInterface<Node> { + + public void verify(Node n) { + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/Token.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,87 @@ +package parser; + +public class Token<Node> { + + + public String name; + public TokenID type; + public Node value; + public TokenID syntax; + public int order; + public static int count = 0; + + public Token(String str) { + this(str,TokenID.UNKNOWN,0); + } + + public Token(String s, TokenID b) { + this(s, b, b.order); + } + + public Token(String s, TokenID b, int order) { + this.name=s; + this.type = b; + this.order = order; + count++; + } + + + public String name() { + return name; + } + + public boolean isVariable() { + return type==TokenID.VARIABLE; + } + + public void setVariable(boolean b) { + type = TokenID.VARIABLE; + } + + public boolean isVariableType() { + return type.isVariable(); + } + + public Node value() { + return value; + } + + public void setValue(Node v) { + value = v; + } + + public void setMacro(Node m) { + type = TokenID.MACRO; + value = m; + } + + public String toString() { + return "Token("+name+","+type+")"; + } + + public String realName() { + if (type==TokenID.MACRO) { + return value.toString(); + } else { + return name; + } + } + + public void setInfix() { + syntax = TokenID.INFIX; + } + + public boolean isInfix() { + return syntax==TokenID.INFIX; + } + + + public void setPrefix() { + syntax = TokenID.PREFIX; + } + + public boolean isPrefix() { + return syntax==TokenID.PREFIX; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/parser/TokenID.java Thu Jan 17 03:20:48 2008 +0900 @@ -0,0 +1,48 @@ +package parser; + +public enum TokenID { + + Space (0,0), + Or (600,0), + And (500,0), // Comma + Not (50,0), + Paren (0,0), + CloseParen (0,0), + CurParen (0,0), + CloseCurParen (0,0), + Exists (50,2), + True (0,1), + False (0,0), + UNKNOWN (1,0), + VARIABLE (1,0), + PREDICATE (1,0), + MACRO (1,0), + Any (0,0), + Chop (900,4), Next(60,3), Empty(0,5), NUMBER(0,0), + STRING(0,0), INFIX(100,0), PREFIX(50,0), NULL(0,0), + Length(50,0), Closure(50,0), Projection(50,6), + Exec(50,0), Include(50,0), Period(0,0), Less(50,0), Define(50,0), + ALIAS(0,0), BParen(0,0),CloseBParen(0,0), Question(0,0), Colon(0,0); + + public final int order; + public final int hash; + + TokenID(int order,int hash) { + this.order = order; + this.hash = hash; + } + + public boolean isVariable() { + switch(this) { + case VARIABLE: case PREDICATE: case MACRO: case INFIX: + return true; + default: + return false; + } + } + + public boolean isInfix() { + return this==INFIX; + } + +}