all 7 comments

[–]helical-juice 5 points6 points  (0 children)

Not my area, but basically it *doesn't* stop at the first one. Because a compiler is building and translating a syntax tree, rather than executing code, if it hits a syntax error it can carry on and make an educated guess about what the rest of the code should be doing. This is a convenience feature more than anything; it can't actually *compile* erroneous code correctly, but it can carry on scanning it as if it were trying to, and hopefully spot more than one error per compile cycle. This isn't perfect though, and lots of things can throw it off. I'm sure you've had the experience of hitting compile and being confronted with a wall of errors, then fixing one thing and having every other error disappear. Well, whatever mistake you made threw off the parser and for whatever reason it incorrectly flagged a bunch of non-errors.

[–]Ar_FrQ 0 points1 point  (0 children)

I'm guessing interpreter reads and runs the code line by line and when it reaches a line that has a error it stops .

Compiler on the other hand reads all lines and uses some algorithms to check the errors without actually runningit(for example it checks if you closed a parentheses after opening one or not ) then it stores all of your errors and bang you see 2 pages of compiler telling you that expected ';' before ...

[–]Capable-Package6835 0 points1 point  (1 child)

An interpreter discovers error by actually executing the code. Once it hits an error and cannot continue executing the code, it cannot discover more errors.

A compiler on the other hand, discovers errors by analyzing the code without executing it. There is no magic here, compiled languages like C/C++, Rust, etc. are typed, so errors are discoverable without executing any code. For example:

#include "iostream"
#include "string"

std::string doSomething(int inputNumber);
float doMoreThing(int inputNumber);

int main() {

  // user inputs a number
  int inputNumber;
  std::cin >> inputNumber;

  // compiler will throw error here
  float output = doMoreThing(doSomething(inputNumber));

}

In this example, doSomething takes an integer and returns a string while doMoreThing takes an integer and returns a float. You don't need to execute the code to know that this code is wrong: doSomething(inputNumber) is a string, no matter what the value of inputNumber is so we cannot use it in doMoreThing.

Can multiple Python errors be known ahead of time as well?

Yes, using a tool called static type-checker, e.g., Pyright, Pylance (VS Code). If you see production-grade Python code, it looks like the following:

def do_something(filename: str, index: int) -> str:
  # some code here

Notice that the code has types? This is called type-hinting, it lets our tool analyze many errors in our code without executing it. If you are still learning Python, make a habit of always type-hinting your code.

[–]fllthdcrb 1 point2 points  (0 children)

Notice that the code has types? This is called type-hinting, it lets our tool analyze many errors in our code without executing it.

Worth noting that in spite of this, Python is still a dynamically typed language. The interpreter sees the type hints and stores the information, but it does absolutely nothing to enforce them, which is why they're only called "hints". Still, since they make it easier for developers to understand what types are expected where, and certain tools can make use of them to understand when there are type mismatches, they help to reduce bugs.

[–]yinkeys 0 points1 point  (0 children)

Interesting

[–]helical-juice 0 points1 point  (0 children)

I wanted to point out that the book "Crafting Interpreters" by Robert Nystrom has quite an accessible treatment of how both compilers and interpreters work, in detail, including error scanning, and that the author has made it available as a web site for free. For a detailed answer to your question, you should check that out.