class Noir::CSharpLexer

Overview

CSharpLexer is a hand-rolled structural lexer for C# source, modelled on Noir::PhpLexer. The C# analyzers count {/}/(/) per line with no string awareness (line.count('{') - line.count('}')), so a single } or ( inside a string literal truncates a method block (dropping callees) or makes a signature run away (dropping parameters). This lexer masks every non-code region in one linear pass so those counters can run over code only.

Handles the C# string zoo:

Every literal is masked to spaces (newlines preserved, length unchanged), so the structural helpers below are plain depth counters over @masked. The source is materialised once into an Array(Char) for O(1) indexing, keeping the scan O(n) on multi-byte (e.g. CJK-commented) source.

Defined in:

minilexers/csharp_lexer.cr

Constructors

Instance Method Summary

Constructor Detail

def self.new(source : String) #

[View source]

Instance Method Detail

def code_lines : Array(String) #

[View source]
def code_source : String #

The source with comments only blanked to spaces — string and char literals are kept intact. Line count and every column index are preserved, so a caller can keep reporting the original line number while scanning text that a commented-out (or documentation-example) route can no longer reach.

#masked_lines is the wrong tool for that job: it also blanks the string literals a route extractor needs to read. ASP.NET Core's own source carries /// app.MapGet("/from-route/{id}", …) inside <example> XML docs, which a raw scan happily emits as an endpoint.


[View source]
def in_code?(pos : Int32) : Bool #

[View source]
def masked : Array(Char) #

[View source]
def masked_lines : Array(String) #

The masked source split into lines, parallel to source.lines. Strings, comments and char literals are blanked, so the C# analyzers can run their existing per-line line.count('{') / line.count('(') counters over masked_lines[i] (structure) while emitting lines[i] (real text).


[View source]
def matching_delimiter(open_pos : Int32) : Int32 | Nil #

[View source]
def skip_ranges : Array(Range(Int32, Int32)) #

[View source]
def statement_end(start_pos : Int32) : Int32 #

Index just after the top-level ; at or after start_pos, or the source size when none is found.


[View source]
def tokens : Array(CSharpToken) #

[View source]