Skip to content

Source Positions

Every node has a SourcePosition recording where in the source it came from. Set it during parsing and every message raised against the node afterwards can point back at the original source, however many passes later it happens.

Nodes default to SourcePosition.None, which is the right value for a node that was generated programmatically rather than parsed from anything.

Source Files

A SourceFile is a named piece of source with a length. Two implementations are provided:

  • TextFile holds the source as a string, split into Lines for reporting.
  • BinaryFile holds the source as bytes, for assemblers and tools that work over binary input.

Both can be constructed from a FileInfo, from a Stream, or from the content directly. Streams are read to the end and left open.

var source = new TextFile(new FileInfo("MySource.code"));  // Contains "50 + 60".
var image = new BinaryFile("rom.bin", romBytes);

Files are compared by type and name, not by content, so two TextFiles for the same path are equal.

Positions

Ask the file for the positions within it. TextFile.CreatePosition takes the start index and length of the position along with zero based line and column indices; BinaryFile.CreatePosition just takes the start index and length. Both also have CreateEntireFilePosition for a position covering the whole file, which is handy for messages about the file as a whole.

expression.SourcePosition = source.CreatePosition(0, 7, 0, 0);  // startIndex, length, startLineIndex, startColumnIndex.
fifty.SourcePosition = source.CreatePosition(0, 2, 0, 0);
sixty.SourcePosition = source.CreatePosition(5, 2, 0, 5);

Indices are validated on construction, so a position can never point outside its file.

SourceFilePosition<TSelf, TFile> is the base for both position types and provides File, StartIndex, Length and the exclusive EndIndex. TextFilePosition adds line and column information, both zero based as Index and one based as Number for display, along with StartLine and the Text of the position itself.

Combining Positions

A node built from several tokens usually wants a position covering all of them. Combine, or the + operator, produces a position spanning both operands and everything between them:

var whole = left.SourcePosition + operatorPosition + right.SourcePosition;

Both positions must be from the same file, and must be the same kind of position; combining across files or mixing text and binary positions throws an ArgumentException.

CreateZeroWidthPrefix gives a zero length position at the start of an existing one. That is what you want to point at "just before this token" — a missing semicolon or an unclosed bracket belongs at the position where the expected thing should have been, not on top of whatever was found instead.

Overlaps reports whether two positions in the same file intersect. Zero length positions overlap only when strictly inside another position, so a zero width position sitting at the start or end index of another does not count as overlapping, and two zero length positions never overlap.

Text Positions in Messages

ITextSourcePosition marks the positions that can render their own source for a message, which currently means TextFilePosition. The MessageFormatter tests for this interface when highlighting is enabled, so binary positions are still used as a message prefix but produce no highlighted line. See Messages for the formatting options.