Repository navigation
nitls: Improve resiliency to parsing errors #2865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
40df2f3
9c3efc4
5c23b50
2da2ef6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| # This file is part of NIT ( http://www.nitlanguage.org ). | ||
| # | ||
| # Licensed 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. | ||
|
|
||
| # Load only the header of the modules: module declaration and imports | ||
| # | ||
| # The AST of the loaded modules have only what's needed for the importation | ||
| # hierarchy and no classes, top-level functions, top-level code, or top-level | ||
| # foreign code bodies. This parsing is fast and robust to syntax errors | ||
| # in the body of the modules. | ||
| module loader_header_only | ||
|
|
||
| import loader | ||
|
|
||
| redef class Lexer | ||
| # Tokens already read ahead of the parser, to be returned next | ||
| private var ahead = new Array[Token] | ||
|
|
||
| # Location of the first token of the body | ||
| private var header_end: nullable Location = null | ||
|
|
||
| # Normal token stream up to the imports, then return EOF | ||
| # | ||
| # Look ahead for a definition or block opening as a way to detect that | ||
| # we're done with imports. The look ahead allows to skip over loose tokens | ||
| # and keywords before the definition markers like visibility and `redef`. | ||
| redef fun get_token | ||
| do | ||
| var header_end = header_end | ||
| if header_end == null then | ||
| if ahead.not_empty then return ahead.shift | ||
|
|
||
| var t = super | ||
| if t == null then return null | ||
| if not is_body_start(t) then return t | ||
|
|
||
| header_end = t.location | ||
| self.header_end = header_end | ||
| end | ||
|
|
||
| return new EOF.init_tk(header_end) | ||
| end | ||
|
|
||
| # Is `t` the first token of the body? Looking ahead as needed | ||
| private fun is_body_start(t: Token): Bool | ||
| do | ||
| if not t.is_definition_prefix then return t.starts_body | ||
|
|
||
| var n = scan_significant | ||
| ahead.add n | ||
| while n.can_be_loose or n.is_definition_prefix do | ||
| n = scan_significant | ||
| ahead.add n | ||
| end | ||
| return not n isa TKwimport | ||
| end | ||
|
|
||
| # Scan the next raw token that is not an ignored blank space | ||
| private fun scan_significant: Token | ||
| do | ||
| var t = scan_token | ||
| while t == null do t = scan_token | ||
| return t | ||
| end | ||
| end | ||
|
|
||
| redef class Token | ||
| private fun starts_body: Bool do return false | ||
| end | ||
|
|
||
| # Definitions | ||
|
|
||
| redef class TKwclass | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwabstract | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwinterface | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwenum | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwsubset | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwextern | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwmeth | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwinit | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwtype | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwin | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| # Statements opening a block | ||
|
|
||
| redef class TKwif | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwwhile | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwfor | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwloop | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwdo | ||
| redef fun starts_body do return true | ||
| end | ||
|
|
||
| redef class TKwwith | ||
| redef fun starts_body do return true | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |
| module nitls | ||
|
|
||
| import modelbuilder | ||
| import loader_header_only | ||
| import ordered_tree | ||
| import console | ||
|
|
||
|
|
@@ -206,6 +207,15 @@ if opt_source.value then | |
| end | ||
| end | ||
|
|
||
| if opt_make.value then | ||
| # The modules that could not be parsed didn't result in a mmodule that | ||
| # could be listed above. List them here from the collected errors. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume the idea here is to genuinely inject buggy modules as dependency and let the rest of the make compilation chain deal with that.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes exactly, any error reported by nitls when building the dependency list for make would be lost. So we inject back known unparsable files so the real call to nitc reports the errors to the user. At this point we print two lists, files linked to a type-checked |
||
| for error in mb.parse_errors do | ||
| var file = error.location.file | ||
| if file != null then print file.filename.escape_to_mk | ||
| end | ||
| end | ||
|
|
||
| if opt_package.value then | ||
| var mpackages = new Array[MPackage] | ||
| for m in mmodules do | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # This file is part of NIT ( http://www.nitlanguage.org ). | ||
| # | ||
| # Licensed 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. | ||
|
|
||
| # The imports can be found, despite the syntax error below | ||
| import base_simple_import | ||
|
|
||
| fun foo(label: Int) do end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # This file is part of NIT ( http://www.nitlanguage.org ). | ||
| # | ||
| # Licensed 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. | ||
|
|
||
| import base_simple_import | ||
|
|
||
| # Syntax error in the header | ||
| import 5 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # This file is part of NIT ( http://www.nitlanguage.org ). | ||
| # | ||
| # Licensed 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. | ||
|
|
||
| module nitls_header_annotations is | ||
| ldflags("-lm", | ||
| "-lpthread") | ||
| end | ||
|
|
||
| import end | ||
| import base_simple | ||
|
|
||
| # The annotations of an import can span lines too | ||
| import base_simple_import is | ||
| conditional(base_simple) | ||
| end | ||
|
|
||
| class NitlsHeaderAnnotations | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # This file is part of NIT ( http://www.nitlanguage.org ). | ||
| # | ||
| # Licensed 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. | ||
|
|
||
| module nitls_header_redef | ||
|
|
||
| import end | ||
|
|
||
| # The grammar accepts `redef` before an import, should it really? | ||
| redef import base_simple3 | ||
|
|
||
| redef class A | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this end up being an infinite loop ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't,
scan_tokenreturns null only on comments and some whitespaces. It will always return an EOF token for the end of file.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The doc on
scan_tokenandget_tokenwas barebone, I updated it to be clearer on the expected tokens and the redef logic.