Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ indent_size = 4
insert_final_newline = false
trim_trailing_whitespace = true

[*.sln]
indent_style = tab
[*.slnx]
indent_size = 2

[*.{csproj,vbproj,vcxproj,vcxproj.filters}]
indent_size = 2
Expand Down
26 changes: 14 additions & 12 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Cache packages
uses: actions/cache@v2
uses: actions/cache@v5
with:
path: ${{ env.NUGET_PACKAGES }}
key: ${{ runner.os }}-nuget-${{ hashFiles('.config/dotnet-tools.json', '**/packages.lock.json') }}
key: ${{ runner.os }}-nuget-${{ hashFiles('dotnet-tools.json', 'src/Directory.Packages.props') }}
restore-keys: ${{ runner.os }}-nuget-

- name: Setup .NET 6.x
uses: actions/setup-dotnet@v1
- name: Setup .NET SDK (global.json + .NET 8 for tests)
uses: actions/setup-dotnet@v5
with:
dotnet-version: 6.0.300
global-json-file: global.json
dotnet-version: 8.0.x

- name: Integration Tests
shell: bash
Expand All @@ -55,21 +56,22 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Cache packages
uses: actions/cache@v2
uses: actions/cache@v5
with:
path: ${{ env.NUGET_PACKAGES }}
key: ${{ runner.os }}-nuget-${{ hashFiles('.config/dotnet-tools.json', '**/packages.lock.json') }}
key: ${{ runner.os }}-nuget-${{ hashFiles('dotnet-tools.json', 'src/Directory.Packages.props') }}
restore-keys: ${{ runner.os }}-nuget-

- name: Setup .NET 6.x
uses: actions/setup-dotnet@v1
- name: Setup .NET SDK (global.json + .NET 8 for tests)
uses: actions/setup-dotnet@v5
with:
dotnet-version: 6.0.300
global-json-file: global.json
dotnet-version: 8.0.x

- name: Integration Tests
shell: bash
Expand Down
13 changes: 7 additions & 6 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Cache packages
uses: actions/cache@v2
uses: actions/cache@v5
with:
path: ${{ env.NUGET_PACKAGES }}
key: ${{ runner.os }}-nuget-${{ hashFiles('.config/dotnet-tools.json', '**/packages.lock.json') }}
key: ${{ runner.os }}-nuget-${{ hashFiles('dotnet-tools.json', 'src/Directory.Packages.props') }}
restore-keys: ${{ runner.os }}-nuget-

- name: Setup .NET 6.x
uses: actions/setup-dotnet@v1
- name: Setup .NET SDK (global.json + .NET 8 for tests)
uses: actions/setup-dotnet@v5
with:
dotnet-version: 6.0.300
global-json-file: global.json
dotnet-version: 8.0.x

- name: Publish
shell: bash
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ dotnet-install.sh*
.vs/
.vscode/
launchSettings.json
*.sln.ide/

# Rider
src/.idea/**/workspace.xml
Expand All @@ -41,7 +40,6 @@ src/.idea/**/uiDesigner.xml
# User-specific files
*.suo
*.user
*.sln.docstates
*.userprefs
*.GhostDoc.xml
*StyleCop.Cache
Expand Down
83 changes: 83 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# AGENTS.md

Guidance for AI coding agents and contributors working in this repository.

## Overview

Appy.Parsing is an open-source .NET library, published to nuget.org, that turns raw text into
objects. A regex-based lexer splits text into tokens and a parser combines tokens into objects;
fluent builders define the grammar. Usage lives in [README.md](README.md), design in
[docs/Architecture.md](docs/Architecture.md).

## Build Commands

```bash
dotnet tool restore # cake, minver-cli, gpr
dotnet cake # Default target: clean, build, test, pack into .artifacts/
dotnet test src/Appy.Parsing.slnx # tests only
dotnet test src/Appy.Parsing.slnx --filter "FullyQualifiedName~CalculatorTest"
```

- The cake build treats warnings as errors.
- `config.yml` lists the projects cake builds and their role (`Package` or `Test`). A new
project not listed there is skipped by the build.
- Package versions live in `src/Directory.Packages.props` (central package management); a
`PackageReference` in a csproj carries no `Version`. The repo `nuget.config` restores from
nuget.org only.
- Never run the `Publish` cake target or `dotnet nuget push` locally; CI publishes.

## Structure

| Path | Purpose |
|------|---------|
| `src/Appy.Parsing/` | The library (`net10.0;net8.0`): `Builder/`, `Lexers/`, `Parsers/` |
| `src/Appy.Parsing.Tests/` | xUnit v3 tests (`net10.0;net8.0`, one run per library TFM) |
| `src/Directory.Build.props` | Shared build settings, package metadata |
| `src/Directory.Build.targets` | MinVer and package validation settings |
| `src/Directory.Packages.props` | Package versions |
| `global.json` | .NET 10 SDK version |
| `build.cake`, `functions.cake`, `config.yml` | Cake build |
| `.github/workflows/` | `ci.yaml` on pull requests, `publish.yaml` on push to `master` or a tag |
| `docs/` | Architecture and plans |

## Versioning and Publishing

- [MinVer](https://github.com/adamralph/minver) derives the version from git tags
(`MAJOR.MINOR.PATCH`, no `v` prefix). Untagged commits get a `-preview.0.N` suffix.
- The MinVer minimum major.minor (`2.0`) is set in both `src/Directory.Build.targets` and
`build.cake`; keep them in sync.
- Pack runs package validation against the last release: `PackageValidationBaselineVersion` in
`src/Directory.Build.targets`, suppressions in `src/Appy.Parsing/CompatibilitySuppressions.xml`.
After each release, move the baseline to that version and drop suppressions that no longer
apply.
- A push to `master` that touches `src/` publishes a preview package; a tag publishes that
version. Do not create tags or releases unless a maintainer asks.
- Dropping a target framework or public API is a breaking change: call it out in the PR.

## Tests

- xUnit v3 (`xunit.v3` 3.2.2, `xunit.runner.visualstudio` 3.1.5, run through VSTest,
`GitHubActionsTestLogger` 2.4.1) with FluentAssertions 7. Stay on FluentAssertions 7: 8 has a
commercial licence.
- Tests build real lexers and parsers through the builders; no mocks.
- Add or update a test with every behaviour change, and write it first.

## Code Style

- `.editorconfig`: 4 spaces, CRLF; 2 spaces for XML, JSON and YAML.
- Match the existing code: `_camelCase` private fields, lower camel case private methods
(`populateUnmatchedPart`), no explicit `private` modifier.
- Public types and members carry XML doc comments.

## Git Conventions

- Branches: `feat/…`, `fix/…`, `docs/…`, `chore/…`, from `master`.
- Commits: conventional commits, `type(scope): subject`, imperative, lower case, no trailing dot
([CONTRIBUTING.md](CONTRIBUTING.md#commit)). Breaking changes go in a `BREAKING CHANGE:` footer.
- No `Co-Authored-By` or tool attribution trailers.
- Public repository: never commit secrets, private feeds or internal links.

## Documentation

- [docs/Architecture.md](docs/Architecture.md): start here
- [docs/plans/](docs/plans/): implementation plans (`NNN-slug.md`)
15 changes: 15 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

@./AGENTS.md

## Build & Test

```bash
dotnet tool restore
dotnet cake # clean, build, test, pack into .artifacts/
dotnet test src/Appy.Parsing.slnx # tests only
```

Never run the `Publish` cake target or `dotnet nuget push`; publishing happens in CI only.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ It also has a number of Builders that allow you to easily define a grammar and b

## Table of Contents

- [Supported frameworks](#supported-frameworks)
- [Installing](#installing)
- [Usage](#usage)

## Supported frameworks

Appy.Parsing targets `net10.0` and `net8.0`.

Version 2.0 drops `net6.0` and `netstandard2.0`, so it no longer runs on .NET Framework. Projects on those frameworks can stay on [1.1.0](https://www.nuget.org/packages/Appy.Parsing/1.1.0).

## Installing

Install using the [Appy.Parsing NuGet package](https://www.nuget.org/packages/Appy.Parsing):
Expand Down
15 changes: 8 additions & 7 deletions build.cake
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#addin nuget:?package=YamlDotNet&version=8.1.2
#addin nuget:?package=YamlDotNet&version=16.2.0
#addin nuget:?package=System.Xml.XDocument&version=4.3.0
#addin nuget:?package=Cake.MinVer&version=2.0.0
#addin nuget:?package=Cake.Yaml&version=3.1.1
#addin nuget:?package=Cake.MinVer&version=4.0.0
#addin nuget:?package=Cake.Yaml&version=6.0.0

#load "./functions.cake"

Expand All @@ -14,6 +14,7 @@ var configFilePath = "config.yml";
var taskConfigManager = new ProjectTaskConfigurationManager();
var projectDescriptors = ProjectLoader.Load(Context, configFilePath, basePath, configuration).Projects;
var version = MinVer(settings => settings
.WithMinimumMajorMinor("2.0")
.WithDefaultPreReleasePhase("preview")
.WithVerbosity(MinVerVerbosity.Info));

Expand Down Expand Up @@ -41,7 +42,7 @@ Task("Restore")
DotNetRestore(basePath,
new DotNetRestoreSettings
{
Verbosity = DotNetCoreVerbosity.Minimal
Verbosity = DotNetVerbosity.Minimal
});
});

Expand All @@ -59,7 +60,7 @@ Task("Build-Project")
Configuration = configuration,
NoRestore = true,
NoIncremental = context.HasArgument("rebuild"),
MSBuildSettings = new DotNetCoreMSBuildSettings()
MSBuildSettings = new DotNetMSBuildSettings()
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
});
}
Expand Down Expand Up @@ -104,7 +105,7 @@ Task("Package")
NoRestore = true,
NoBuild = true,
OutputDirectory = artifactsPath,
MSBuildSettings = new DotNetCoreMSBuildSettings()
MSBuildSettings = new DotNetMSBuildSettings()
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
});
}
Expand Down Expand Up @@ -163,7 +164,7 @@ Task("Publish-Package-NuGet")

context.Information("Publishing {0} to Nuget", nugetPkgFilePath);

DotNetCoreNuGetPush(nugetPkgFilePath, new DotNetCoreNuGetPushSettings
DotNetNuGetPush(nugetPkgFilePath, new DotNetNuGetPushSettings
{
Source = "https://api.nuget.org/v3/index.json",
ApiKey = apiKey,
Expand Down
44 changes: 44 additions & 0 deletions docs/Architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Appy.Parsing: Architecture

## Overview

Appy.Parsing converts raw text into objects in two stages. A `Lexer` runs one combined regular
expression (one named group per `Tokenizer`) and turns each match into a token object; text that
matches nothing is kept as a string unless the lexer ignores it. A `Parser` then applies its
`TokenParser` rules in registration order. Each rule encodes the token sequence as a string of type
identifiers, matches its pattern against it and replaces the matched run with the object it
creates, repeating until the rule no longer matches.

`LexerBuilder` and `ParserBuilder` are the public entry points: fluent builders that declare the
grammar. Build and test guidance lives in [AGENTS.md](../AGENTS.md).

## System Diagram

```mermaid
graph LR
Text["Input text"] --> Lexer
LexerBuilder -->|creates| Lexer
Lexer -->|tokens| Parser
ParserBuilder -->|creates| Parser
Parser -->|applies TokenParser rules in order| Result["object[] or T"]
```

## Key Patterns

### Builders define the grammar

`LexerBuilder.Match*` registers tokenizers (regex, enum values via `[Match]`, dictionaries).
`ParserBuilder.Match*` registers token rules with `MatchOptions` (`AllInOrder`, `AnyOrder`,
`AtLeastOne`) that expand into one rule per accepted token combination. Builders compose with
`CombineWith`.

### Cached tokenization

`Lexer` caches the token array per input expression in a `ConcurrentDictionary`, so repeated
expressions skip the regex pass.

## Plans

| Plan | Status | Summary |
|------|--------|---------|
| [001 .NET 10 Migration](plans/001-net10-migration.md) | In Progress | Target net10.0 and net8.0; align build and CI |
Loading
Loading