diff --git a/docs/reference.md b/docs/reference.md index aca0d58c..520e97bf 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -303,6 +303,7 @@ Exception: | General.ignore | α → unit | "ignore x" always returns `unit`. The function evaluates its argument but throws away the value. | | General.op o | (β → γ) (α → β) → α → γ | "f o g" is the function composition of `f` and `g`. Thus, `(f o g) a` is equivalent to `f (g a)`. | | Interact.use | string → unit | "use f" loads source text from the file named `f`. | +| Interact.useSilently | string → unit | "useSilently f" loads source text from the file named `f`, without printing to stdout. | | List.nil | α list | "nil" is the empty list. | | List.null | α list → bool | "null l" returns `true` if the list `l` is empty. | | List.length | α list → int | "length l" returns the number of elements in the list `l`. | diff --git a/src/main/java/net/hydromatic/morel/Main.java b/src/main/java/net/hydromatic/morel/Main.java index 205e3a57..232310fd 100644 --- a/src/main/java/net/hydromatic/morel/Main.java +++ b/src/main/java/net/hydromatic/morel/Main.java @@ -213,7 +213,7 @@ public void run() { final Map outBindings = new LinkedHashMap<>(); final Shell shell = new Shell(this, env, echoLines, outLines, outBindings); session.withShell(shell, outLines, session1 -> - shell.run(session1, new BufferingReader(in))); + shell.run(session1, new BufferingReader(in), echoLines, outLines)); out.flush(); } @@ -236,7 +236,8 @@ static class Shell implements Session.Shell { this.bindingMap = bindingMap; } - void run(Session session, BufferingReader in2) { + void run(Session session, BufferingReader in2, Consumer echoLines, + Consumer outLines) { final MorelParserImpl parser = new MorelParserImpl(in2); final SubShell subShell = new SubShell(main, echoLines, outLines, bindingMap, env0); @@ -280,7 +281,7 @@ void run(Session session, BufferingReader in2) { } } - @Override public void use(String fileName, Pos pos) { + @Override public void use(String fileName, boolean silent, Pos pos) { throw new UnsupportedOperationException(); } @@ -310,7 +311,7 @@ static class SubShell extends Shell { super(main, env0, echoLines, outLines, outBindings); } - @Override public void use(String fileName, Pos pos) { + @Override public void use(String fileName, boolean silent, Pos pos) { outLines.accept("[opening " + fileName + "]"); File file = new File(fileName); if (!file.isAbsolute()) { @@ -324,9 +325,13 @@ static class SubShell extends Shell { + ", No such file or directory]"); throw new Codes.MorelRuntimeException(Codes.BuiltInExn.ERROR, pos); } - try (FileReader fileReader = new FileReader(file); - BufferedReader bufferedReader = new BufferedReader(fileReader)) { - run(main.session, new BufferingReader(bufferedReader)); + final Consumer echoLines2 = silent ? line -> {} : echoLines; + final Consumer outLines2 = silent ? line -> {} : outLines; + try (FileReader in = new FileReader(file); + Reader bufferedReader = + buffer(main.idempotent ? stripOutLines(in) : in)) { + run(main.session, new BufferingReader(bufferedReader), echoLines2, + outLines2); } catch (IOException e) { e.printStackTrace(); } diff --git a/src/main/java/net/hydromatic/morel/Shell.java b/src/main/java/net/hydromatic/morel/Shell.java index 517df014..22ff1e42 100644 --- a/src/main/java/net/hydromatic/morel/Shell.java +++ b/src/main/java/net/hydromatic/morel/Shell.java @@ -527,7 +527,7 @@ private class Use implements Session.Shell { this.bindings = bindings; } - @Override public void use(String fileName, Pos pos) { + @Override public void use(String fileName, boolean silent, Pos pos) { outLines.accept("[opening " + fileName + "]"); File file = new File(fileName); if (!file.isAbsolute()) { diff --git a/src/main/java/net/hydromatic/morel/compile/BuiltIn.java b/src/main/java/net/hydromatic/morel/compile/BuiltIn.java index defbebe7..a83d49cb 100644 --- a/src/main/java/net/hydromatic/morel/compile/BuiltIn.java +++ b/src/main/java/net/hydromatic/morel/compile/BuiltIn.java @@ -201,6 +201,13 @@ public enum BuiltIn { INTERACT_USE("Interact", "use", "use", ts -> ts.fnType(STRING, UNIT)), + /** Function "Interact.useSilently" of type "string → unit" + * + *

"useSilently f" loads source text from the file named `f`, + * without printing to stdout. */ + INTERACT_USE_SILENTLY("Interact", "useSilently", "useSilently", ts -> + ts.fnType(STRING, UNIT)), + /** Constant "String.maxSize", of type "int". * *

"The longest allowed size of a string". */ diff --git a/src/main/java/net/hydromatic/morel/eval/Codes.java b/src/main/java/net/hydromatic/morel/eval/Codes.java index 7a4025e9..90261164 100644 --- a/src/main/java/net/hydromatic/morel/eval/Codes.java +++ b/src/main/java/net/hydromatic/morel/eval/Codes.java @@ -341,7 +341,12 @@ public static Code orElse(Code code0, Code code1) { }; /** @see BuiltIn#INTERACT_USE */ - private static final Applicable INTERACT_USE = new InteractUse(Pos.ZERO); + private static final Applicable INTERACT_USE = + new InteractUse(Pos.ZERO, false); + + /** @see BuiltIn#INTERACT_USE_SILENTLY */ + private static final Applicable INTERACT_USE_SILENTLY = + new InteractUse(Pos.ZERO, true); /** Removes wrappers, in particular the one due to * {@link #wrapRelList(Code)}. */ @@ -358,18 +363,21 @@ public static Code strip(Code code) { /** Implements {@link BuiltIn#INTERACT_USE}. */ private static class InteractUse extends ApplicableImpl implements Positioned { - InteractUse(Pos pos) { + private final boolean silent; + + InteractUse(Pos pos, boolean silent) { super(BuiltIn.INTERACT_USE, pos); + this.silent = silent; } @Override public Applicable withPos(Pos pos) { - return new InteractUse(pos); + return new InteractUse(pos, silent); } @Override public Object apply(EvalEnv env, Object arg) { final String f = (String) arg; final Session session = (Session) env.getOpt(EvalEnv.SESSION); - session.use(f, pos); + session.use(f, silent, pos); return Unit.INSTANCE; } } @@ -2783,6 +2791,7 @@ public static Applicable aggregate(Environment env0, Code aggregateCode, .put(BuiltIn.IGNORE, IGNORE) .put(BuiltIn.GENERAL_OP_O, GENERAL_OP_O) .put(BuiltIn.INTERACT_USE, INTERACT_USE) + .put(BuiltIn.INTERACT_USE_SILENTLY, INTERACT_USE_SILENTLY) .put(BuiltIn.OP_CARET, OP_CARET) .put(BuiltIn.OP_CONS, OP_CONS) .put(BuiltIn.OP_DIV, OP_DIV) diff --git a/src/main/java/net/hydromatic/morel/eval/Session.java b/src/main/java/net/hydromatic/morel/eval/Session.java index 8368b593..da1ed0c9 100644 --- a/src/main/java/net/hydromatic/morel/eval/Session.java +++ b/src/main/java/net/hydromatic/morel/eval/Session.java @@ -95,8 +95,8 @@ public void withoutHandlingExceptions(Consumer consumer) { } } - public void use(String fileName, Pos pos) { - shell.use(fileName, pos); + public void use(String fileName, boolean silent, Pos pos) { + shell.use(fileName, silent, pos); } public void handle(MorelException e, StringBuilder buf) { @@ -105,7 +105,7 @@ public void handle(MorelException e, StringBuilder buf) { /** Callback to implement "use" command. */ public interface Shell { - void use(String fileName, Pos pos); + void use(String fileName, boolean silent, Pos pos); /** Handles an exception. Particular implementations may re-throw the * exception, or may format the exception to a buffer that will be added to @@ -136,7 +136,7 @@ private enum Shells implements Shell { } }; - @Override public void use(String fileName, Pos pos) { + @Override public void use(String fileName, boolean silent, Pos pos) { throw new UnsupportedOperationException(); } } diff --git a/src/test/resources/script/builtIn.smli b/src/test/resources/script/builtIn.smli index 0bfd2021..8a3fecb9 100644 --- a/src/test/resources/script/builtIn.smli +++ b/src/test/resources/script/builtIn.smli @@ -28,7 +28,8 @@ General; > val it = {ignore=fn,`op o`=fn} > : {ignore:'a -> unit, `op o`:('b -> 'c) * ('d -> 'b) -> 'd -> 'c} Interact; -> val it = {use=fn} : {use:string -> unit} +> val it = {use=fn,useSilently=fn} +> : {use:string -> unit, useSilently:string -> unit} List; > val it = > {all=fn,app=fn,at=fn,collate=fn,concat=fn,drop=fn,exists=fn,filter=fn, @@ -2703,7 +2704,8 @@ Sys.env (); > [("EQUAL","order"),("GREATER","order"), > ("General", > "{ignore:forall 'a. 'a -> unit, `op o`:forall 'a 'b 'c. ('b -> 'c) * ('a -> 'b) -> 'a -> 'c}"), -> ("Interact","{use:string -> unit}"),("LESS","order"), +> ("Interact","{use:string -> unit, useSilently:string -> unit}"), +> ("LESS","order"), > ("List", > "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> order) -> 'a list * 'a list -> order, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a option, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> ('a * 'a list) option, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b option) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, `op @`:forall 'a. 'a list * 'a list -> 'a list, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"), > ("Math", @@ -2766,7 +2768,8 @@ env (); > [("EQUAL","order"),("GREATER","order"), > ("General", > "{ignore:forall 'a. 'a -> unit, `op o`:forall 'a 'b 'c. ('b -> 'c) * ('a -> 'b) -> 'a -> 'c}"), -> ("Interact","{use:string -> unit}"),("LESS","order"), +> ("Interact","{use:string -> unit, useSilently:string -> unit}"), +> ("LESS","order"), > ("List", > "{all:forall 'a. ('a -> bool) -> 'a list -> bool, app:forall 'a. ('a -> unit) -> 'a list -> unit, at:forall 'a. 'a list * 'a list -> 'a list, collate:forall 'a. ('a * 'a -> order) -> 'a list * 'a list -> order, concat:forall 'a. 'a list list -> 'a list, drop:forall 'a. 'a list * int -> 'a list, exists:forall 'a. ('a -> bool) -> 'a list -> bool, filter:forall 'a. ('a -> bool) -> 'a list -> 'a list, find:forall 'a. ('a -> bool) -> 'a list -> 'a option, foldl:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, foldr:forall 'a 'b. ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, getItem:forall 'a. 'a list -> ('a * 'a list) option, hd:forall 'a. 'a list -> 'a, last:forall 'a. 'a list -> 'a, length:forall 'a. 'a list -> int, map:forall 'a 'b. ('a -> 'b) -> 'a list -> 'b list, mapPartial:forall 'a 'b. ('a -> 'b option) -> 'a list -> 'b list, nil:forall 'a. 'a list, nth:forall 'a. 'a list * int -> 'a, null:forall 'a. 'a list -> bool, `op @`:forall 'a. 'a list * 'a list -> 'a list, partition:forall 'a. ('a -> bool) -> 'a list -> 'a list * 'a list, rev:forall 'a. 'a list -> 'a list, revAppend:forall 'a. 'a list * 'a list -> 'a list, tabulate:forall 'a. int * (int -> 'a) -> 'a list, take:forall 'a. 'a list * int -> 'a list, tl:forall 'a. 'a list -> 'a list}"), > ("Math", diff --git a/src/test/resources/script/relational.smli b/src/test/resources/script/relational.smli index 54f4472a..c14bb371 100644 --- a/src/test/resources/script/relational.smli +++ b/src/test/resources/script/relational.smli @@ -25,6 +25,10 @@ Sys.set ("printLength", 64); Sys.set ("stringDepth", ~1); > val it = () : unit +useSilently "scott.smli"; +> [opening scott.smli] +> val it = () : unit + let val emp0 = {id = 100, name = "Fred", deptno = 10} in #id emp0 end; > val it = 100 : int diff --git a/src/test/resources/script/scott.smli b/src/test/resources/script/scott.smli new file mode 100644 index 00000000..da176e15 --- /dev/null +++ b/src/test/resources/script/scott.smli @@ -0,0 +1,99 @@ +(* + * Licensed to Julian Hyde under one or more contributor license + * agreements. See the NOTICE file distributed with this work + * for additional information regarding copyright ownership. + * Julian Hyde licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + *) +Sys.set ("printLength", 64); +> val it = () : unit + +val scott = { + bonus = [] : {comm:real, ename:string, job:string, sal:real} list, + emp = [ + {comm = 0.0, deptno = 20, empno = 7369, ename = "SMITH", hiredate = "1980-12-16", job = "CLERK", mgr = 7902, sal = 800.0}, + {comm = 300.0, deptno = 30, empno = 7499, ename = "ALLEN", hiredate = "1981-02-19", job = "SALESMAN", mgr = 7698, sal = 1600.0}, + {comm = 500.0, deptno = 30, empno = 7521, ename = "WARD", hiredate = "1981-02-21", job = "SALESMAN", mgr = 7698, sal = 1250.0}, + {comm = 0.0, deptno = 20, empno = 7566, ename = "JONES", hiredate = "1981-02-03", job = "MANAGER", mgr = 7839, sal = 2975.0}, + {comm = 1400.0, deptno = 30, empno = 7654, ename = "MARTIN", hiredate = "1981-09-27", job = "SALESMAN", mgr = 7698, sal = 1250.0}, + {comm = 0.0, deptno = 30, empno = 7698, ename = "BLAKE", hiredate = "1981-01-04", job = "MANAGER", mgr = 7839, sal = 2850.0}, + {comm = 0.0, deptno = 10, empno = 7782, ename = "CLARK", hiredate = "1981-06-08", job = "MANAGER", mgr = 7839, sal = 2450.0}, + {comm = 0.0, deptno = 20, empno = 7788, ename = "SCOTT", hiredate = "1987-04-18", job = "ANALYST", mgr = 7566, sal = 3000.0}, + {comm = 0.0, deptno = 10, empno = 7839, ename = "KING", hiredate = "1981-11-16", job = "PRESIDENT", mgr = 0, sal = 5000.0}, + {comm = 0.0, deptno = 30, empno = 7844, ename = "TURNER", hiredate = "1981-09-07", job = "SALESMAN", mgr = 7698, sal = 1500.0}, + {comm = 0.0, deptno = 20, empno = 7876, ename = "ADAMS", hiredate = "1987-05-22", job = "CLERK", mgr = 7788, sal = 1100.0}, + {comm = 0.0, deptno = 30, empno = 7900, ename = "JAMES", hiredate = "1981-12-02", job = "CLERK", mgr = 7698, sal = 950.0}, + {comm = 0.0, deptno = 20, empno = 7902, ename = "FORD", hiredate = "1981-12-02", job = "ANALYST", mgr = 7566, sal = 3000.0}, + {comm = 0.0, deptno = 10, empno = 7934, ename = "MILLER", hiredate = "1982-01-22", job = "CLERK", mgr = 7782, sal = 1300.0}], + dept = [ + {deptno = 10, dname = "ACCOUNTING", loc = "NEW YORK"}, + {deptno = 20, dname = "RESEARCH", loc = "DALLAS"}, + {deptno = 30, dname = "SALES", loc = "CHICAGO"}, + {deptno = 40, dname = "OPERATIONS", loc = "BOSTON"}], + salgrade = [ + {grade = 1, hisal = 1200.0, losal = 700.0}, + {grade = 2, hisal = 1400.0, losal = 1201.0}, + {grade = 3, hisal = 2000.0, losal = 1401.0}, + {grade = 4, hisal = 3000.0, losal = 2001.0}, + {grade = 5, hisal = 9999.0, losal = 3001.0}] +}; +> val scott = +> {bonus=[], +> dept= +> [{deptno=10,dname="ACCOUNTING",loc="NEW YORK"}, +> {deptno=20,dname="RESEARCH",loc="DALLAS"}, +> {deptno=30,dname="SALES",loc="CHICAGO"}, +> {deptno=40,dname="OPERATIONS",loc="BOSTON"}], +> emp= +> [ +> {comm=0.0,deptno=20,empno=7369,ename="SMITH",hiredate="1980-12-16", +> job="CLERK",mgr=7902,sal=800.0}, +> {comm=300.0,deptno=30,empno=7499,ename="ALLEN",hiredate="1981-02-19", +> job="SALESMAN",mgr=7698,sal=1600.0}, +> {comm=500.0,deptno=30,empno=7521,ename="WARD",hiredate="1981-02-21", +> job="SALESMAN",mgr=7698,sal=1250.0}, +> {comm=0.0,deptno=20,empno=7566,ename="JONES",hiredate="1981-02-03", +> job="MANAGER",mgr=7839,sal=2975.0}, +> {comm=1400.0,deptno=30,empno=7654,ename="MARTIN",hiredate="1981-09-27", +> job="SALESMAN",mgr=7698,sal=1250.0}, +> {comm=0.0,deptno=30,empno=7698,ename="BLAKE",hiredate="1981-01-04", +> job="MANAGER",mgr=7839,sal=2850.0}, +> {comm=0.0,deptno=10,empno=7782,ename="CLARK",hiredate="1981-06-08", +> job="MANAGER",mgr=7839,sal=2450.0}, +> {comm=0.0,deptno=20,empno=7788,ename="SCOTT",hiredate="1987-04-18", +> job="ANALYST",mgr=7566,sal=3000.0}, +> {comm=0.0,deptno=10,empno=7839,ename="KING",hiredate="1981-11-16", +> job="PRESIDENT",mgr=0,sal=5000.0}, +> {comm=0.0,deptno=30,empno=7844,ename="TURNER",hiredate="1981-09-07", +> job="SALESMAN",mgr=7698,sal=1500.0}, +> {comm=0.0,deptno=20,empno=7876,ename="ADAMS",hiredate="1987-05-22", +> job="CLERK",mgr=7788,sal=1100.0}, +> {comm=0.0,deptno=30,empno=7900,ename="JAMES",hiredate="1981-12-02", +> job="CLERK",mgr=7698,sal=950.0}, +> {comm=0.0,deptno=20,empno=7902,ename="FORD",hiredate="1981-12-02", +> job="ANALYST",mgr=7566,sal=3000.0}, +> {comm=0.0,deptno=10,empno=7934,ename="MILLER",hiredate="1982-01-22", +> job="CLERK",mgr=7782,sal=1300.0}], +> salgrade= +> [{grade=1,hisal=1200.0,losal=700.0},{grade=2,hisal=1400.0,losal=1201.0}, +> {grade=3,hisal=2000.0,losal=1401.0},{grade=4,hisal=3000.0,losal=2001.0}, +> {grade=5,hisal=9999.0,losal=3001.0}]} +> : {bonus:{comm:real, ename:string, job:string, sal:real} list, +> dept:{deptno:int, dname:string, loc:string} list, +> emp: +> {comm:real, deptno:int, empno:int, ename:string, hiredate:string, +> job:string, mgr:int, sal:real} list, +> salgrade:{grade:int, hisal:real, losal:real} list} + +(*) End scott.smli