LogicalFile (LogicalFile v1.1.1)

View Source

LogicalFile

One file from many

LogicalFile is a way of creating a logical representation of a unit of lines of text (e.g. a source code file) supplied by one or more backing files, presumably separate files on disk. It also provides for a system of macros that can transform the logical text.

A typical use case for LogicalFile would be to implement a compiler that has #include style functionality. The compiler works on the whole text but can translate logical line numbers back to specific files and local line numbers (for example when an error occurs it can pinpoint the specific file and line the error arose in).

Summary

Functions

assemble/2 returns a LogicalFile composed of the Sections specified in the second argument. This is mainly intended for internal use when modifying a LogicalFile during macro processing.

contains_source?/2 returns true if at least one section from the given LogicalFile originates from the specified source_path.

insert/3 inserts a new Section into the LogicalFile at the specified logical line number at_line and containing the contents of the source_path.

last_line_number/1 returns the line number of the last line in the specified LogicalFile.

line/2 returns the specified logical line number from the LogicalFile at lno.

lines/1 returns a list of all lines in the LogicalFile in line number order.

lines/2 takes a LogicalFile and a range of logical line numbers and returns a list of tuples in the form {file, line} for the corresponding lines.

partition_sections/2 accepts a list of Sections and a logical line number at_line representing an insertion point. It returns a tuple {sections_before, insert_section, sections_after} by finding the Section containing at_line and partitioning the remaining Sections around it.

read/3 returns a new LogicalFile containing Sections that represent the contents of the file specified by source_path relative to base_path and as modified by the macros it is initialised with.

resolve_line/2 takes a logical line number logical_lno and returns a tuple {file, local_line_no} representing the file and file line number that logical line represents.

section_including_line/2 returns the Section that contains the logical line lno.

sections_in_order/1 takes the Sections backing a LogicalFile and returns them as a list, ordered by the range of logical line numbers they represent.

sections_to_map/1 takes a list of Sections and returns a Map whose keys are the logical line number ranges of the sections, mapped to the corresponding sections.

size/1 returns the number of lines in the LogicalFile.

update_line/3 replaces the content of line lno in the specified LogicalFile by passing the current contents of the line to the specified transformation function. This function is expected to return the new contents of the line.

Types

t()

@type t() :: %LogicalFile{base_path: nil | binary(), sections: map()}

Functions

assemble(base_path, sections)

assemble/2 returns a LogicalFile composed of the Sections specified in the second argument. This is mainly intended for internal use when modifying a LogicalFile during macro processing.

contains_source?(logical_file, source_path)

contains_source?/2 returns true if at least one section from the given LogicalFile originates from the specified source_path.

Examples

iex> file = LogicalFile.read("test/support", "main.source")
iex> invalid_path = Path.expand("test/support/player.source")
iex> assert not LogicalFile.contains_source?(file, invalid_path)
iex> valid_path = Path.expand("test/support/main.source")
iex> assert LogicalFile.contains_source?(file, valid_path)

insert(file, source_path, at_line)

insert/3 inserts a new Section into the LogicalFile at the specified logical line number at_line and containing the contents of the source_path.

It guarantees that all sections and the logical file remains contiguous.

Examples

last_line_number(file)

last_line_number/1 returns the line number of the last line in the specified LogicalFile.

Examples

iex> alias LogicalFile.Section
iex> file = LogicalFile.read("test/support", "main.source")
iex> assert 11 = LogicalFile.last_line_number(file)

line(file, lno)

line/2 returns the specified logical line number from the LogicalFile at lno.

Example

iex> file = LogicalFile.read("test/support", "main.source")
iex> assert "%(include.source)" = LogicalFile.line(file, 6)

lines(file)

lines/1 returns a list of all lines in the LogicalFile in line number order.

lines(file, logical_line_range)

lines/2 takes a LogicalFile and a range of logical line numbers and returns a list of tuples in the form {file, line} for the corresponding lines.

partition_sections(sections, at_line)

partition_sections/2 accepts a list of Sections and a logical line number at_line representing an insertion point. It returns a tuple {sections_before, insert_section, sections_after} by finding the Section containing at_line and partitioning the remaining Sections around it.

read(base_path, source_path, macros \\ [])

read/3 returns a new LogicalFile containing Sections that represent the contents of the file specified by source_path relative to base_path and as modified by the macros it is initialised with.

Macros should implement the LogicalFile.Macro behaviours and should be specified as a list of tuples of the form {module, [options keyword list]}. See LogicalFile.Macro for further details.

Examples

iex> file = LogicalFile.read("test/support", "main.source")
iex> assert 11 = LogicalFile.size(file)

resolve_line(file, logical_lno)

resolve_line/2 takes a logical line number logical_lno and returns a tuple {file, local_line_no} representing the file and file line number that logical line represents.

Examples

iex> alias LogicalFile.Macros.Include
iex> file = LogicalFile.read("test/support", "main.source")
iex> path = Path.expand("test/support/main.source")
iex> assert {^path, 1} = LogicalFile.resolve_line(file, 1)

section_including_line(file, lno)

section_including_line/2 returns the Section that contains the logical line lno.

Examples

iex> alias LogicalFile.Section
iex> section1 = Section.new("test/support/main.source")
iex> section2 = Section.new("test/support/include.source") |> Section.shift(Section.size(section1))
iex> map = LogicalFile.assemble("test/support", [section1, section2])
iex> assert ^section1 = LogicalFile.section_including_line(map, section1.range.first)
iex> assert ^section2 = LogicalFile.section_including_line(map, section2.range.first)

sections_in_order(logical_file)

sections_in_order/1 takes the Sections backing a LogicalFile and returns them as a list, ordered by the range of logical line numbers they represent.

sections_to_map(sections)

sections_to_map/1 takes a list of Sections and returns a Map whose keys are the logical line number ranges of the sections, mapped to the corresponding sections.

size(logical_file)

size/1 returns the number of lines in the LogicalFile.

Examples

iex> file = LogicalFile.read("test/support", "main.source")
iex> 11 = LogicalFile.size(file)

update_line(file, lno, fun)

update_line/3 replaces the content of line lno in the specified LogicalFile by passing the current contents of the line to the specified transformation function. This function is expected to return the new contents of the line.

Examples

iex> assert "                 " =
...>  LogicalFile.read("test/support", "main.source")
...>  |> LogicalFile.update_line(6, fn line -> String.duplicate(" ", String.length(line)) end)
...>  |> LogicalFile.line(6)