1

We need to make something of a lexical analyzer, and I have had some trouble with a particular function of mine, useLoad, or more accurately, what happens in Main after useLoad is used.

I figured out that that was because...for some reason, buffer=keyboard.nextLine() is throwing the error, because it's not getting more input from the keyboard for some reason. I thought that .nextLine() should force it to get more input from the user. And I don't know why it's throwing that exception specifically after this one particular method. It can do other methods just fine and not lose its ability to read. Is it because I have a variable called keyboard in another object and closed it? That seems doubtful. Just tried changing the name. Didn't make a difference.

Variables used but not declared in the below code: Keywords[0] is the String "load ". initial = the scanner string that's passed in to the function. offset = a counter variable, to see how far in to the line we've read.

The useLoad function (which is what I think is messing up somehow), is at the bottom, but I included everything it runs through (with each method separated by a horizontal rule), in chronological order, just in case I'm just not seeing what's going on.

public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in); //the scanner for keyboard
        int i = 0;
        String buffer ="";
        boolean loopControl == true;
        SymbolTable mySym = new SymbolTable();

        System.out.println("READY FOR INPUT\n");

        while (loopControl == true){
            //read in the line
            buffer = "";
            buffer = keyboard.nextLine();
            if(!mySym.checkStop(buffer)){ //if we didn't stop
                mySym.primary(buffer);
            }
            else{//if we did stop
                closeLoop();
            }

            if (i >= 55){
                loopControl = false;
                System.out.println(("You have gone over the limit ("+i+" lines) per execution. Please continue by running this program again.").toUpperCase());
                //just a safety precaution...you know... in case closeLoop doesn't work
            }
            i++;
        }


        keyboard.close();

    }

    if(initial.substring(0, Keywords[0].length()).equals(Keywords[0])){ //Load
        //if this is working as expected, then we simply need to do what the keyword says to do.
        offset += Keywords[0].length(); //we have moved this much deeper in to the line
        useLoad(offset, initial);
        offset = 0; //just make sure, once we are done with the line, we start back at the start of the next line.
        return; //we found what we were looking for, get out.
    }

private void useLoad(int offsetIn, String readIn) {
        double doubIn = 0;
        //now get the value of the 
        Scanner keyboard = new Scanner(System.in); //the scanner for keyboard
            System.out.println("\nENTER VALUE FOR " + readIn.toUpperCase());
             doubIn = keyboard.nextDouble();    
        keyboard.close();

        variables.create(readIn.substring(offsetIn), doubIn);
    }
  • 1
    Where do you initialize the 'loopControl'? – beatrice Sep 30 '16 at 01:24
  • 1
    `keyboard.close();` closes the keyboard (duh) so you can't type or read anything. (Actually that's not *quite* correct, but close enough) – user253751 Sep 30 '16 at 01:32
  • @beatrice It was a global variable in main's class (so that other methods could mess with it, like closeLoop() ). That was why it wasn't copied. I forgot about that. I added it back in as a local variable, since it doesn't make much difference. – Carrot Head Sep 30 '16 at 01:39
  • @immibis Thanks. That was from when I used ctrl+z 1 fewer times than I thought I did. I fixed that, and am still getting the same error – Carrot Head Sep 30 '16 at 01:41
  • Stop calling keyboard.close(). – matt Sep 30 '16 at 07:18

2 Answers2

0

You close the keyboard each time round the loop. Therefore the second time around you read from a closed keyboard object.

A quick look at the documentation for Scanner.nextLine contains the news that it might throw:

NoSuchElementException - if no line was found
N00b Pr0grammer
  • 4,503
  • 5
  • 32
  • 46
dave
  • 11
  • 1
  • Oops, sorry. That was from when I used ctrl+z 1 fewer times than I thought I did. I fixed that, and am still getting the same error. – Carrot Head Sep 30 '16 at 01:38
  • You probably should call hasNextLine before calling nextLine. I'm not particularly familiar with Scanner, but generally Java methods follow a haveNext/next pattern. The doc for hasNextLine says it may block for input, for nextLine it says no such thing. – dave Sep 30 '16 at 01:46
  • hmm? I thought that nextLine and likewise nextDouble had it read in from the keyboard (or however the scanner was initialized). I'll try that. – Carrot Head Sep 30 '16 at 01:50
  • OK! I'll update my question. Turns out that the problem is, in fact, with the main method. Found out when I tried doing what you suggested, and (because it had no line after the the loop, I guess), it tried to pass in an empty string to mySym.checkStop(). – Carrot Head Sep 30 '16 at 01:57
0

I think I've figured out your problem.

Java docs for both Java 7 and 8 include this line in Scanner's close method documentation:

If this scanner has not yet been closed then if its underlying readable also implements the Closeable interface then the readable's close method will be invoked.

Looking into the docs for System, I've found that System.in is of type InputStream which, you guessed it, implements Closeable. The close method documentation for InputStream says that it does nothing; however, InputStream is abstract and close is not marked as final, which means it can be overridden. System.in returns an InputStream which could potentially - and clearly does - do something.

So the problem is, you are creating multiple Scanners with System.in, and each time you close any one of them, you close System.in, rendering it useless!

This problem has actually been discussed in another question here, with a solution given. That said, for your program I would suggest one of two approaches:

  • The first approach is mentioned there: Either use a pre-made wrapper class or make your own, which accepts an InputStream in its constructor. Have this class' InputStream implementation call all the methods of its wrapped object, except for the close method which is a no-sell, and then pass Wrapper(System.in) to Scanner instead of System.in directly. However, I would be wary of taking this approach except in very specific circumstances, because any time you use one of these wrappers you would need to remember to close its wrapped object at the end of its use, unless it's something like System.in.
  • Second approach: Use a runner class in your program, and initialize the scanner there. Pass the scanner into the required objects during construction for them to hold a reference to, and then allow them to complete their required tasks without closing Scanner within the class. Once the exit condition is specified, return to the runner class and close Scanner from there.
Community
  • 1
  • 1
MutantOctopus
  • 3,431
  • 4
  • 22
  • 31
  • To be fair, @immibis actually said this before me, but it wasn't very clearly stated due to the fact that your input reader is actually named `keyboard`. They were trying to say what I'm saying now - that calling a `System.in` Scanner's `close` method "closes" the keyboard itself – MutantOctopus Sep 30 '16 at 17:49
  • I am thinking you might be right.. I had similar suspicions. Working on my Lisp program is giving me a headache. I'll try implementing what you said in the morning. – Carrot Head Oct 01 '16 at 00:40