diff --git a/CHANGELOG.md b/CHANGELOG.md index 98386526b582faaaa5f112d3fbc56ea102f31b7d..5b5dd824f84c7a78b1c2f03a347778be06a9ebc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # REAL changelog +## Version [0.5](https://git.ecdf.ed.ac.uk/pguaglia/real/commits/0.5) (2021-09-28) + +#### New features + +- In-app help on internal commands. Issuing the command `.help` at REAL's prompt + shows the list of available commands, along with their usage and description. +- Atomic selection conditions now support less-than comparisons using the newly + introduced `<` operator. This compares strings lexicographically, but behaves + differently on strings that represent integer values. More precisely, if the + two strings being compared can be *both* parsed as integers, then they are + compared as integers; otherwise, lexicographic comparison is used. As a side + effect, we lose the ability to compare lexicographically strings that consist + only of digits. This will be rectified once proper types are introduced. + +#### Changed functionality + +- Renaming is now defined as an operation that only accepts one replacement, in + place of a list thereof. That possibility is retained as a syntactic shortcut, + which is parsed as sequence of nested renamings. For example, + `<R>[A->B,B->C](table)` is short for `<R>[B->C](<R>[A->B](table))`. + +#### API improvements + +- Get list of replacements in the renaming operation. +- Getters for conditions and terms. +- New abstract superclass for unary operations. + ## Version [0.4](https://git.ecdf.ed.ac.uk/pguaglia/real/commits/0.4) (2020-09-08) #### New features diff --git a/data/sample-db/Account.csv b/data/sample-db/Account.csv deleted file mode 100644 index 355a3065b2cf13945ab555a9316b5dd698e10cdf..0000000000000000000000000000000000000000 --- a/data/sample-db/Account.csv +++ /dev/null @@ -1,4 +0,0 @@ -Number,Branch,CustID,Balance -04132,Edinburgh,cust1,0 -59687,London,cust2,1000 -19283,Edinburgh,cust2,500 diff --git a/data/sample-db/Customer.csv b/data/sample-db/Customer.csv deleted file mode 100644 index cbecb0c8951a1d42b1e09df087216b90152cb6b4..0000000000000000000000000000000000000000 --- a/data/sample-db/Customer.csv +++ /dev/null @@ -1,4 +0,0 @@ -ID,Name,City,Age -cust1,Renton,Edinburgh,24 -cust2,Watson,London,32 -cust3,Holmes,London,35 diff --git a/pom.xml b/pom.xml index 104840470e08ab95c055c5c7f0f48248811c51a9..a3dd27dc528bf0069ff1bb8a049a897d58f17b5a 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ <groupId>uk.ac.ed.pguaglia</groupId> <artifactId>real</artifactId> - <version>0.4</version> + <version>0.5</version> <name>real</name> <url>https://git.ecdf.ed.ac.uk/pguaglia/real</url> @@ -62,6 +62,19 @@ </dependencies> <build> + <resources> + <resource> + <directory>src/main/resources</directory> + <excludes> + <exclude>**/*.jar</exclude> + </excludes> + <includes> + <include>welcome.txt</include> + <include>help.txt</include> + <include>syntax.txt</include> + </includes> + </resource> + </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> diff --git a/src/main/antlr4/uk/ac/ed/pguaglia/real/parsing/RA.g4 b/src/main/antlr4/uk/ac/ed/pguaglia/real/parsing/RA.g4 index 884c56914d2012cc1dc482c53495cbc60c091a2f..ea2ced0804bb733646822d67c04e0b86f353a90c 100755 --- a/src/main/antlr4/uk/ac/ed/pguaglia/real/parsing/RA.g4 +++ b/src/main/antlr4/uk/ac/ed/pguaglia/real/parsing/RA.g4 @@ -22,11 +22,16 @@ NAME )* ; -CONSTANT +STRING : '\'' .+? '\'' ; +INTEGER +: + DIGIT+ +; + WHITESPACE : [ \t\r\n]+ -> skip @@ -102,7 +107,8 @@ attribute_list constant : - CONSTANT + STRING + | INTEGER ; term @@ -131,6 +137,7 @@ condition | condition AND condition # Conjunction | condition OR condition # Disjunction | term '=' term # Equality + | term '<' term # LessThan ; expression diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/BinaryCondition.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/BinaryCondition.java index 5b4a5a4eff255f6edd04fb37cf84b5ca7ea179c3..17406509105002cffb4be09985063e44f1d80825 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/BinaryCondition.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/BinaryCondition.java @@ -13,6 +13,14 @@ public abstract class BinaryCondition extends Condition { this.rightCondition = right; } + public Condition getLeftCondition() { + return leftCondition; + } + + public Condition getRightCondition() { + return rightCondition; + } + @Override public String toString() { return String.format( "%s %s %s", diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/BinaryOperation.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/BinaryOperation.java index 39fa5af7060972bbce8fd4a0f018c41ff85283bf..0729914186e4caa3c763b163bab040f15715856b 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/BinaryOperation.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/BinaryOperation.java @@ -11,6 +11,13 @@ import uk.ac.ed.pguaglia.real.db.Table; public abstract class BinaryOperation extends Expression { protected Expression leftOperand; + protected Expression rightOperand; + + public BinaryOperation ( Expression left, Expression right, Expression.Type type ) { + super(type); + this.leftOperand = left; + this.rightOperand = right; + } public Expression getLeftOperand() { return leftOperand; @@ -20,14 +27,6 @@ public abstract class BinaryOperation extends Expression { return rightOperand; } - protected Expression rightOperand; - - public BinaryOperation ( Expression left, Expression right, Expression.Type type ) { - super(type); - this.leftOperand = left; - this.rightOperand = right; - } - @Override public String toString() { return String.format( "%s %s %s", diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/Distinct.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/Distinct.java index 2342cb43e2f3460f150933ac864791b00b47a06c..09437532ae42733eb704f6fa5f6971875beeef85 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/Distinct.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/Distinct.java @@ -8,13 +8,10 @@ import uk.ac.ed.pguaglia.real.db.Schema; import uk.ac.ed.pguaglia.real.db.SchemaException; import uk.ac.ed.pguaglia.real.db.Table; -public class Distinct extends Expression { +public class Distinct extends UnaryOperation { - private Expression expr; - - public Distinct(Expression expr) { - super(Type.DISTINCT); - this.expr = expr; + public Distinct(Expression operand) { + super(operand, Type.DISTINCT); } @Override @@ -22,35 +19,35 @@ public class Distinct extends Expression { String tree = "%1$s\n%2$s |\n%2$s +--- %3$s"; String conn = this.getType().getConnective(); String s; - if (expr.getType() == Expression.Type.BASE) { - s = expr.toSyntaxTreeString("", schema); + if (operand.getType() == Expression.Type.BASE) { + s = operand.toSyntaxTreeString("", schema); s += "\n" + prefix; } else { - s = expr.toSyntaxTreeString(prefix + " ", schema); + s = operand.toSyntaxTreeString(prefix + " ", schema); } return String.format(tree, conn, prefix, s); } @Override public Set<String> signature(Schema schema) throws SchemaException { - return expr.signature(schema); + return operand.signature(schema); } @Override public Table execute(Database db, boolean bags) throws DatabaseException { signature(db.schema()); - return bags ? expr.execute(db,true).distinct() : expr.execute(db,false); + return bags ? operand.execute(db,true).distinct() : operand.execute(db,false); } @Override public String toString() { return String.format( "%s( %s )", this.getType().getConnective(), - expr.toString() ); + operand.toString() ); } @Override public Set<String> getBaseNames() { - return expr.getBaseNames(); + return operand.getBaseNames(); } } diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/Equality.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/Equality.java index 2e7214d1761255ddf587c2925e72070419e022ed..9e5049beb7349934971be3adcd4c6328b92cb256 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/Equality.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/Equality.java @@ -15,6 +15,14 @@ public class Equality extends Condition { this.right = right; } + public Term getLeftTerm() { + return left; + } + + public Term getRightTerm() { + return right; + } + @Override public String toString() { return String.format( "%s = %s", diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/LessThan.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/LessThan.java new file mode 100644 index 0000000000000000000000000000000000000000..3edbe0a32740f95b6954835fccd3f490df8e367b --- /dev/null +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/LessThan.java @@ -0,0 +1,53 @@ +package uk.ac.ed.pguaglia.real.lang; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class LessThan extends Condition { + + private Term left; + private Term right; + + public LessThan(Term left, Term right) { + super(Condition.Type.COMPARISON); + this.left = left; + this.right = right; + } + + public Term getLeftTerm() { + return left; + } + + public Term getRightTerm() { + return right; + } + + @Override + public String toString() { + return String.format("%s < %s", left.toString(), right.toString()); + } + + @Override + public Set<String> signature() { + var sign = new HashSet<String>(); + if (left.isAttribute()) { + sign.add(left.getValue()); + } + if (right.isAttribute()) { + sign.add(right.getValue()); + } + return sign; + } + + @Override + public boolean satisfied(String[] record, Map<String, Integer> attr) { + String v1 = left.getValue(record, attr); + String v2 = right.getValue(record, attr); + try { + return Integer.valueOf(v1) < Integer.valueOf(v2); + } catch (NumberFormatException e) { + return v1.compareTo(v2) < 0; + } + } +} diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/Negation.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/Negation.java index 2e0965abb66af6705e9aaf52a0cd0bc955ad7e92..0a2bc2c8b536174546e5cd4e50140131a1558e60 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/Negation.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/Negation.java @@ -12,6 +12,10 @@ public class Negation extends Condition { this.cond = c; } + public Condition getCondition() { + return cond; + } + @Override public String toString() { return String.format("%s( %s )", getType().getConnective(), cond.toString()); diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/Projection.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/Projection.java index e4b1b5113c296a45771ac4e210a8caa418146c59..ac1990e7d043d3bb65965d449cd5c3b5beeac732 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/Projection.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/Projection.java @@ -11,14 +11,12 @@ import uk.ac.ed.pguaglia.real.db.SchemaException; import uk.ac.ed.pguaglia.real.db.Table; -public class Projection extends Expression { +public class Projection extends UnaryOperation { - private Expression operand; private List<String> attributes; public Projection ( List<String> attributes, Expression operand ) { - super(Expression.Type.PROJECTION); - this.operand = operand; + super(operand, Expression.Type.PROJECTION); this.attributes = attributes; } diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/Renaming.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/Renaming.java index 18e0e713bb8d2244eb41fe395eb2466cff187e1f..d95cb352d7126d58e05cc274a1057eb9346d7787 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/Renaming.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/Renaming.java @@ -1,6 +1,7 @@ package uk.ac.ed.pguaglia.real.lang; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -15,14 +16,12 @@ import uk.ac.ed.pguaglia.real.db.SchemaException; import uk.ac.ed.pguaglia.real.db.Table; -public class Renaming extends Expression { +public class Renaming extends UnaryOperation { - private Expression operand; private Map<String,String> replacements; public Renaming (Map<String,String> replacements, Expression operand) throws ReplacementException { - super(Expression.Type.RENAMING); - this.operand = operand; + super(operand,Expression.Type.RENAMING); if (Utils.isInjective(replacements)) { this.replacements = replacements; } else { @@ -87,4 +86,8 @@ public class Renaming extends Expression { public Set<String> getBaseNames() { return operand.getBaseNames(); } + + public Map<String,String> getReplacements() { + return new HashMap<String, String>(replacements); + } } diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/Selection.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/Selection.java index 8954542d555f7e8d4facc26a17f56b4099f795e9..08a8f44b36ec4705cf38723e10da57131aa3404e 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/Selection.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/Selection.java @@ -9,23 +9,25 @@ import uk.ac.ed.pguaglia.real.db.Schema; import uk.ac.ed.pguaglia.real.db.SchemaException; import uk.ac.ed.pguaglia.real.db.Table; -public class Selection extends Expression { +public class Selection extends UnaryOperation { - private Expression expr; private Condition cond; - public Selection ( Condition cond, Expression expr ) { - super(Expression.Type.SELECTION); - this.expr = expr; + public Selection ( Condition cond, Expression operand ) { + super(operand,Expression.Type.SELECTION); this.cond = cond; } + public Condition getCondition() { + return cond; + } + @Override public String toString() { return String.format( "%s[%s]( %s )", this.getType().getConnective(), cond.toString(), - expr.toString() ); + operand.toString() ); } @Override @@ -36,14 +38,14 @@ public class Selection extends Expression { .replace("( ", "(") .replace(" )", ")") .replace(" = ", "="); - String s = Utils.toSyntaxTreeString(expr, schema, prefix, " "); + String s = Utils.toSyntaxTreeString(operand, schema, prefix, " "); return String.format(tree, conn, cond, prefix, s); } @Override public Set<String> signature(Schema schema) throws SchemaException { Set<String> condAttrs = cond.signature(); - Set<String> exprAttrs = Utils.clone(expr.signature(schema)); + Set<String> exprAttrs = Utils.clone(operand.signature(schema)); if (exprAttrs.containsAll(condAttrs)) { return exprAttrs; } else { @@ -56,11 +58,11 @@ public class Selection extends Expression { @Override public Table execute(Database db, boolean bags) throws DatabaseException { signature(db.schema()); - return expr.execute(db,bags).select(cond); + return operand.execute(db,bags).select(cond); } @Override public Set<String> getBaseNames() { - return expr.getBaseNames(); + return operand.getBaseNames(); } } diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/Term.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/Term.java index 1d82d544eb6fbec58eba2495be7558a7bd204441..358444b9c494a464fd068155db4d2d19259ea4ed 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/lang/Term.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/Term.java @@ -26,7 +26,8 @@ public class Term { @Override public String toString() { - return constant ? "'" + value + "'" : value; + // TODO: create proper type for integers and remove the regex + return constant && !value.matches("[0-9]+") ? "'" + value + "'" : value; } public String getValue(String[] record, Map<String,Integer> attr) { diff --git a/src/main/java/uk/ac/ed/pguaglia/real/lang/UnaryOperation.java b/src/main/java/uk/ac/ed/pguaglia/real/lang/UnaryOperation.java new file mode 100644 index 0000000000000000000000000000000000000000..53527baf651a48655adc7e6bb301a0f15777b766 --- /dev/null +++ b/src/main/java/uk/ac/ed/pguaglia/real/lang/UnaryOperation.java @@ -0,0 +1,15 @@ +package uk.ac.ed.pguaglia.real.lang; + +public abstract class UnaryOperation extends Expression { + + protected Expression operand; + + public UnaryOperation ( Expression operand, Expression.Type type ) { + super(type); + this.operand = operand; + } + + public Expression getOperand() { + return this.operand; + } +} diff --git a/src/main/java/uk/ac/ed/pguaglia/real/parsing/RealListener.java b/src/main/java/uk/ac/ed/pguaglia/real/parsing/RealListener.java index c6d0f68463e195ea171fada8c847e94e0a5a3e8b..a4347fc4b848bbd94266935bdedd54ed311834e5 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/parsing/RealListener.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/parsing/RealListener.java @@ -3,7 +3,6 @@ package uk.ac.ed.pguaglia.real.parsing; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Stack; import uk.ac.ed.pguaglia.real.lang.BaseExpression; @@ -15,6 +14,7 @@ import uk.ac.ed.pguaglia.real.lang.Distinct; import uk.ac.ed.pguaglia.real.lang.Equality; import uk.ac.ed.pguaglia.real.lang.Expression; import uk.ac.ed.pguaglia.real.lang.Intersection; +import uk.ac.ed.pguaglia.real.lang.LessThan; import uk.ac.ed.pguaglia.real.lang.Negation; import uk.ac.ed.pguaglia.real.lang.Product; import uk.ac.ed.pguaglia.real.lang.Projection; @@ -26,14 +26,24 @@ import uk.ac.ed.pguaglia.real.lang.Union; public class RealListener extends RABaseListener { + private class MapEntry<K,V> { + K key; + V val; + + public MapEntry(K key, V val) { + this.key = key; + this.val = val; + } + } + private Stack<Expression> exprStack = new Stack<Expression>(); private List<String> attrList = null; private Stack<List<String>> attrListStack = new Stack<List<String>>(); private Stack<Term> termStack = new Stack<Term>(); private Stack<Condition> condStack = new Stack<Condition>(); private Stack<String> attrStack = new Stack<String>(); - private Map<String,String> replMap = null; - private Stack<Map<String,String>> replMapStack = new Stack<Map<String,String>>(); + private List<MapEntry<String,String>> replMap = null; + private Stack<List<MapEntry<String,String>>> replMapStack = new Stack<>(); private Expression parsedExpression = null; public Expression parsedExpression() { @@ -97,7 +107,7 @@ public class RealListener extends RABaseListener { @Override public void enterReplacementList(RAParser.ReplacementListContext ctx) { - replMap = new HashMap<String,String>(); + replMap = new ArrayList<MapEntry<String,String>>(); } @Override @@ -136,6 +146,13 @@ public class RealListener extends RABaseListener { condStack.push(new Equality(left, right)); } + @Override + public void exitLessThan(RAParser.LessThanContext ctx) { + Term right = termStack.pop(); + Term left = termStack.pop(); + condStack.push(new LessThan(left, right)); + } + @Override public void exitConjunction(RAParser.ConjunctionContext ctx) { Condition right = condStack.pop(); @@ -167,20 +184,22 @@ public class RealListener extends RABaseListener { public void exitReplacement(RAParser.ReplacementContext ctx) { String v = attrStack.pop(); String k = attrStack.pop(); - if (replMap.containsKey(k)) { - throw new RuntimeException(ctx.getText(), ReplacementException.renamedMoreThanOnce(k)); - } - replMap.put(k, v); + replMap.add(new MapEntry<>(k,v)); } @Override public void exitRenaming(RAParser.RenamingContext ctx) { - Map<String,String> repl = replMapStack.pop(); - Expression expr = exprStack.pop(); - try { - exprStack.push(new Renaming(repl, expr)); - } catch (ReplacementException e) { - throw new RuntimeException(ctx.getText(), e); + var repl = replMapStack.pop(); + var expr = exprStack.pop(); + for (var entry : repl) { + try { + HashMap<String, String> map = new HashMap<>(); + map.put(entry.key, entry.val); + expr = new Renaming(map, expr); + } catch (ReplacementException e) { + throw new RuntimeException(ctx.getText(), e); + } } + exprStack.push(expr); } } diff --git a/src/main/java/uk/ac/ed/pguaglia/real/runtime/CommandLineApp.java b/src/main/java/uk/ac/ed/pguaglia/real/runtime/CommandLineApp.java index a7882dd16e0e61c79bef7baf7607af1db2bb38e0..92d5d2c9154833986df176f8f7d18173e7e732d6 100644 --- a/src/main/java/uk/ac/ed/pguaglia/real/runtime/CommandLineApp.java +++ b/src/main/java/uk/ac/ed/pguaglia/real/runtime/CommandLineApp.java @@ -2,6 +2,7 @@ package uk.ac.ed.pguaglia.real.runtime; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -36,7 +37,18 @@ public final class CommandLineApp { private enum Eval { OFF, SET, BAG } private enum Commands { - QUIT, SCHEMA, TREE, EVAL, DROP, SAVE, LOAD, ADD, TABLES, VIEWS; + HELP, // show help for commands + QUIT, // exit the application + TREE, // show the parse tree [on/off] + EVAL, // set evaluation semantics [set/bag/off] + DROP, // delete table or view + SAVE, // save current database + LOAD, // load database from file + ADD, // create new base table + TABLES, // list tables + VIEWS, // list views + SCHEMA, // print the JSON schema file (private) + SYNTAX, // show accepted RA syntax } private static final String APP_COMMAND = "java -jar <path/to/real-X.Y.Z.jar>"; @@ -84,6 +96,23 @@ public final class CommandLineApp { System.exit(-1); } + // load welcome, help and syntax messages from file + String welcome = null; + String help = null; + String syntax = null; + try { + InputStream input = CommandLineApp.class.getResourceAsStream("/welcome.txt"); + welcome = new String(input.readAllBytes()); + input = CommandLineApp.class.getResourceAsStream("/help.txt"); + help = new String(input.readAllBytes()); + input = CommandLineApp.class.getResourceAsStream("/syntax.txt"); + syntax = new String(input.readAllBytes()); + input.close(); + } catch (IOException e3) { + // TODO Auto-generated catch block + e3.printStackTrace(); + } + Property<Tree> parseTree = new Property<>( Commands.TREE.toString().toLowerCase(), Tree.class, @@ -129,6 +158,7 @@ public final class CommandLineApp { .build(); String prompt = "$> "; + System.out.println(welcome); String viewName = null; mainLoop: do { @@ -149,7 +179,8 @@ public final class CommandLineApp { try { cmd = Commands.valueOf(cmdName.toUpperCase()); } catch (IllegalArgumentException e) { - System.err.println(String.format("Unrecognized command \".%s\"", cmdName)); + System.err.println(String.format("Unrecognized command \".%s\"\n" + + "Type \".help\" to print the list of available commands.", cmdName)); continue mainLoop; } cmdSwitch: switch (cmd) { @@ -158,6 +189,19 @@ public final class CommandLineApp { System.err.println(String.format("WARNING: ignoring \"%s\"", line)); } break mainLoop; + case HELP: + if (line.isBlank() == false) { + System.err.println(String.format("WARNING: ignoring \"%s\"", line)); + } + System.out.println(); // print empty line + System.out.println(help); + continue mainLoop; + case SYNTAX: + if (line.isBlank() == false) { + System.err.println(String.format("WARNING: ignoring \"%s\"", line)); + } + System.out.println(syntax); + continue mainLoop; case TABLES: if (line.isBlank() == false) { System.err.println(String.format("WARNING: ignoring \"%s\"", line)); diff --git a/src/main/resources/help.txt b/src/main/resources/help.txt new file mode 100644 index 0000000000000000000000000000000000000000..d4bf98e781e78971e62a4c5aedf2af58ccf54e5c --- /dev/null +++ b/src/main/resources/help.txt @@ -0,0 +1,38 @@ + .syntax Show the Relational Algebra grammar accepted by REAL. + + .tables List names and attributes of the base tables in the + current database. + + .views List names and definitions of the views in the current + database. + + .help Show this help message. + + .quit Exit REAL's command prompt and go back to the shell + (unsaved changes are discarded). + + .tree [on|off] Turn parse tree on/off [default: off]. + With no arguments, the current setting is shown. + + .eval [set|bag|off] choose query evaluation semantics: + - 'set': set semantics (default) + - 'bag': bag semantics + - 'off': no evaluation + With no arguments, the current setting is shown. + + .add TABLE_NAME(ATTR_LIST) : DATA_FILE + Create a new base table: + - TABLE_NAME is the name of the table; + - ATTR_LIST is a comma-separated list of attribute names; + - DATA_FILE is the path to a CSV file containing the + instance (rows) of the table. + + .drop TABLE_NAME Delete table or view TABLE_NAME from the schema. + + .load PATH Load the database from the JSON file specified by PATH. + + .save [PATH] Save the current database schema to the JSON file + specified by PATH. Without PATH, the current schema + file is used, which is the latest file explicitly + loaded with .load, or the temporary file automatically + created when starting the application. diff --git a/src/main/resources/syntax.txt b/src/main/resources/syntax.txt new file mode 100644 index 0000000000000000000000000000000000000000..b01e85fdb628430cb6ea3367978e990c44c08ab3 --- /dev/null +++ b/src/main/resources/syntax.txt @@ -0,0 +1,70 @@ +┌──────────────────────────────────────────────────────────────────────────────┠+│ RELATIONAL ALGEBRA GRAMMAR │ +└──────────────────────────────────────────────────────────────────────────────┘ + + TABLE_NAME → case-sensitive alphanumeric string that: + ‣ starts with a letter + ‣ may contain the underscore character "_" + ‣ does not contain spaces of any kind + + EXPRESSION → TABLE_NAME # base table (or view) + → <S>[ CONDITION ]( EXPRESSION ) # selection + → <P>[ ATTR_LIST ]( EXPRESSION ) # projection + → <R>[ REPL_LIST ]( EXPRESSION ) # renaming + → EXPRESSION BINARY_OP EXPRESSION # binary operation + → ( EXPRESSION ) # parenthesized expression + + BINARY_OP → <X> # cross product + → <U> # union + → <D> # difference + → <I> # intersection + + ATTR_NAME → same rules as for table names + + STRING → anything enclosed in single quotes + + INTEGER → non-empty sequence of digits + + CONSTANT → STRING + → INTEGER + + TERM → ATTR_NAME + → CONSTANT + + ATTR_LIST → comma-separated list of ATTR_NAMEs + + REPLACEMENT → ATTR_NAME -> ATTR_NAME + + REPL_LIST → comma-separated list of REPLACEMENTs + + CONDITION → TERM = TERM # equality comparison + → CONDITION & CONDITION # conjunction + → CONDITION | CONDITION # disjunction + → ~ CONDITION # negation + → ( CONDITION ) # parenthesized condition + +┌──────────────────────────────────────────────────────────────────────────────┠+│ UNARY OPERATIONS: USAGE OF PROJECTION, SELECTION, RENAMING │ +└──────────────────────────────────────────────────────────────────────────────┘ + + <S> selection + + Usage: <S>[selection-condition](EXPRESSION) + + Example: <S>[A=B](R) + ‣ selects the rows of R where A=B + + <P> projection + + Usage: <P>[comma-separated-list-of-attributes](EXPRESSION) + + Example: <P>[A,B](R) + ‣ projects columns A and B from R + + <R> renaming + + Usage: <R>[comma-separated-list-of-replacements](EXPRESSION) + + Example: <R>[A->B,B->C](R<U>S) + ‣ first renames A to B, then B to C, in the union of R and S + ‣ equivalent to <R>[B->C](<R>[A->B](R<U>S)) diff --git a/src/main/resources/welcome.txt b/src/main/resources/welcome.txt new file mode 100644 index 0000000000000000000000000000000000000000..9f905e3fb2a11640d641a9d8dae57689f8ad4fa9 --- /dev/null +++ b/src/main/resources/welcome.txt @@ -0,0 +1,5 @@ +┌──────────────────────────────────────────────────────────────────────────────┠+│ REAL: an interpreter for Relational Algebra (v0.5) │ +│ Released under the MIT License │ +│ Copyright © 2019-2021 Paolo Guagliardo │ +└──────────────────────────────────────────────────────────────────────────────┘