diff --git a/CHANGELOG.md b/CHANGELOG.md index 9010d67..6bdb7f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## v4.5.0 +### New Features + +- Add `noline` grammar statement to skip emitting `#line` directives + ### Fixes - Fix #line reset directives diff --git a/doc/user_guide.md b/doc/user_guide.md index 70c1dc3..6498563 100644 --- a/doc/user_guide.md +++ b/doc/user_guide.md @@ -383,6 +383,27 @@ module proj.parser; If a module statement is not present, then the generated D module will not contain a module statement and the default module name will be used. +##> `noline` statement - disabling `#line` directives + +By default, Propane emits `#line` directives into the generated output around +user code blocks. +These directives instruct the compiler to report any warnings or errors in the +user code using the file name and line number of the original grammar file, +rather than the generated output file. +This makes it easier to locate the source of a compiler diagnostic in the +grammar file. + +The `noline` statement disables the emission of these `#line` directives. + +``` +noline; +``` + +When the `noline` statement is present, user code blocks are copied to the +generated output without any surrounding `#line` directives. +This can be useful when debugging the generated parser itself, or when the +`#line` directives interfere with other tooling. + ##> `on_tree_node` statement - custom initialization of a token tree node The `on_token_node` statement can be used to provide code that initializes diff --git a/lib/propane/grammar.rb b/lib/propane/grammar.rb index ece158b..cde6004 100644 --- a/lib/propane/grammar.rb +++ b/lib/propane/grammar.rb @@ -88,6 +88,7 @@ class Propane elsif parse_rule_statement! elsif parse_code_block_statement! elsif parse_prefix_statement! + elsif parse_noline_statement! else if @input.size > 25 @input = @input.slice(0..20) + "..." @@ -306,10 +307,12 @@ class Propane if md = consume!(/<<([a-z]*)(.*?)>>\n/m) name, code = md[1..2] code = code.chomp - if code.start_with?("\n") - code = %[#line #{@line_number + 1} "#{@filename}"#{code}\n#linereset\n] - else - code = %[#line #{@line_number} "#{@filename}"\n#{code}\n#linereset\n] + unless @noline + if code.start_with?("\n") + code = %[#line #{@line_number + 1} "#{@filename}"#{code}\n#linereset\n] + else + code = %[#line #{@line_number} "#{@filename}"\n#{code}\n#linereset\n] + end end if @code_blocks[name] @code_blocks[name] += code @@ -328,6 +331,13 @@ class Propane end end + def parse_noline_statement! + if md = consume!(/noline\s*;/) + @noline = true + true + end + end + def parse_pattern! if md = consume!(%r{/}) pattern = "" @@ -352,10 +362,12 @@ class Propane def parse_code_block! if md = consume!(/<<(.*?)>>\n/m) code = md[1].chomp - if code.start_with?("\n") - code = %[#line #{@line_number + 1} "#{@filename}"#{code}\n#linereset\n] - else - code = %[#line #{@line_number} "#{@filename}"\n#{code}\n#linereset\n] + unless @noline + if code.start_with?("\n") + code = %[#line #{@line_number + 1} "#{@filename}"#{code}\n#linereset\n] + else + code = %[#line #{@line_number} "#{@filename}"\n#{code}\n#linereset\n] + end end code end diff --git a/spec/propane/grammar_spec.rb b/spec/propane/grammar_spec.rb index 47f70a0..902b4fd 100644 --- a/spec/propane/grammar_spec.rb +++ b/spec/propane/grammar_spec.rb @@ -133,6 +133,41 @@ EOF expect(o.code).to eq %[#line 7 "test.propane"\n writeln("Hello there");\n#linereset\n] end + it "does not emit #line directives with noline statement" do + input = <> + +token code2 << + writeln("Hello there"); +>> + +tokenid token_with_no_pattern; + +prefix myparser_; +EOF + grammar = Grammar.new(input, "test.propane") + expect(grammar.prefix).to eq "myparser_" + + o = grammar.tokens.find {|token| token.name == "code1"} + expect(o).to_not be_nil + + o = grammar.patterns.find {|pattern| pattern.token == o} + expect(o).to_not be_nil + expect(o.code).to eq %[\n a = b;\n return c;] + + o = grammar.tokens.find {|token| token.name == "code2"} + expect(o).to_not be_nil + + o = grammar.patterns.find {|pattern| pattern.token == o} + expect(o).to_not be_nil + expect(o.code).to eq %[\n writeln("Hello there");] + end + it "supports mode labels" do input = <