Compare commits

..

7 Commits

Author SHA1 Message Date
Will Hawkins c31f9b5c0d Testing Gitea macOS
Continuous Integration / Explore-Gitea-Actions (push) Successful in 5s
Continuous Integration / Library Tests (push) Failing after 6s
Continuous Integration / Library Format Tests (push) Failing after 6s
Continuous Integration / Grammar Tests (push) Failing after 12s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-04 18:23:29 -04:00
Will Hawkins b334e3a87f Testing Gitea macOS
Continuous Integration / Grammar Tests (push) Failing after 6s
Continuous Integration / Library Tests (push) Failing after 6s
Continuous Integration / Explore-Gitea-Actions (push) Successful in 5s
Continuous Integration / Library Format Tests (push) Failing after 7s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-04 18:21:17 -04:00
Will Hawkins 9625fa524d Testing Gitea macOS
Continuous Integration / Library Tests (push) Failing after 5s
Continuous Integration / Library Format Tests (push) Failing after 5s
Continuous Integration / Explore-Gitea-Actions (push) Successful in 5s
Continuous Integration / Grammar Tests (push) Failing after 0s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-04 18:17:39 -04:00
Will Hawkins 6d064c6fd7 Testing Gitea macOS
Continuous Integration / Explore-Gitea-Actions (push) Successful in 5s
Continuous Integration / Library Tests (push) Failing after 5s
Continuous Integration / Grammar Tests (push) Failing after 1s
Continuous Integration / Library Format Tests (push) Failing after 5s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-04 18:16:11 -04:00
Will Hawkins b87d5e5fc6 Testing Gitea macOS
Continuous Integration / Explore-Gitea-Actions (push) Successful in 4s
Continuous Integration / Library Tests (push) Failing after 5s
Continuous Integration / Library Format Tests (push) Failing after 6s
Continuous Integration / Grammar Tests (push) Failing after 4s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-04 18:09:07 -04:00
Will Hawkins 4c49e2b861 Testing Gitea macOS
Continuous Integration / Grammar Tests (push) Failing after 5s
Continuous Integration / Library Tests (push) Failing after 6s
Continuous Integration / Library Format Tests (push) Failing after 6s
Continuous Integration / Explore-Gitea-Actions (push) Successful in 5s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-04 17:43:29 -04:00
Will Hawkins 1b8b505618 Testing Gitea Ubuntu Runner
Continuous Integration / Explore-Gitea-Actions (push) Successful in 25s
Continuous Integration / Library Tests (push) Has been cancelled
Continuous Integration / Library Format Tests (push) Has been cancelled
Continuous Integration / Grammar Tests (push) Failing after 5s
Signed-off-by: Will Hawkins <hawkinsw@obs.cr>
2026-05-04 16:13:06 -04:00
11 changed files with 66 additions and 268 deletions
+20 -2
View File
@@ -6,10 +6,15 @@ jobs:
grammar-tests: grammar-tests:
name: Grammar Tests name: Grammar Tests
runs-on: macos-latest runs-on: macos-latest
env:
GH_TOKEN: ${{ secrets.GITEA_TOKEN }}
ACTION_STEP_DEBUG: true
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
- uses: tree-sitter/setup-action@v2 - uses: cerfcast/setup-action@886f0ab373105625f5af268677bc715050fcc105
with:
tree-sitter-ref: 8d737aa238084f9a51f1e97c195a362419beee8d
# Because our tree-sitter code is in a subdirectory, # Because our tree-sitter code is in a subdirectory,
# and working-directory does not apply to uses, we # and working-directory does not apply to uses, we
# are forced to specify calls to the CI ourselves. # are forced to specify calls to the CI ourselves.
@@ -49,4 +54,17 @@ jobs:
- run: tree-sitter generate - run: tree-sitter generate
working-directory: ./tree-sitter-p4 working-directory: ./tree-sitter-p4
- run: ./ci/format.sh - run: ./ci/format.sh
Explore-Gitea-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v4
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}
- run: echo "🍏 This job's status is ${{ job.status }}."
+1 -3
View File
@@ -22,9 +22,7 @@ import Darwin
@main @main
struct Cli: ParsableCommand { struct Cli: ParsableCommand {
public func run() throws { public func run() throws {
let formatter = FormatterPlain()
let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "Testing") let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "Testing")
let e1 = ErrorWithLocation(sourceLocation: SourceLocation(10, 5), withError: "Oh no") print(e.format())
print(e.append(error: e1).format(formatter))
} }
} }
+9 -16
View File
@@ -16,8 +16,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
public struct Error: Errorable, Equatable, CustomStringConvertible { public struct Error: Errorable, Equatable, CustomStringConvertible {
public func format(_ formatter: any Formattable) -> String { public func format() -> String {
return self._msg return self.description
} }
public func msg() -> String { public func msg() -> String {
@@ -40,10 +40,11 @@ public struct Error: Errorable, Equatable, CustomStringConvertible {
} }
public struct ErrorWithLocation: Errorable, Equatable, CustomStringConvertible { public struct ErrorWithLocation: Errorable, Equatable, CustomStringConvertible {
public func format(_ formatter: any Formattable) -> String { let startFormat: String = "\u{1B}[31;1;4m"
let bold_red = Style(StyleColor.Red, [StyleFormat.Bold]) let endFormat: String = "\u{1B}[0m"
let formatted_location = formatter.formatWithStyle(self.location.description, bold_red)
return formatted_location + ": " + self._msg public func format() -> String {
return startFormat + "\(self.location)" + endFormat + ": \(self._msg)"
} }
public func msg() -> String { public func msg() -> String {
@@ -69,10 +70,8 @@ public struct ErrorWithLocation: Errorable, Equatable, CustomStringConvertible {
} }
public struct Errors: Errorable, CustomStringConvertible { public struct Errors: Errorable, CustomStringConvertible {
public func format(_ formatter: any Formattable) -> String { public func format() -> String {
self.errors.map { error in return self.description
error.format(formatter)
}.joined(separator: "\n")
} }
public func msg() -> String { public func msg() -> String {
@@ -104,12 +103,6 @@ public struct ErrorWithLabel: Errorable {
let label: String let label: String
let error: any Errorable let error: any Errorable
public func format(_ formatter: any Formattable) -> String {
let green = Style(StyleColor.Green)
let formatted_label = formatter.formatWithStyle(self.label, green)
return formatted_label + self.error.format(formatter)
}
public init(_ label: String, _ error: any Errorable) { public init(_ label: String, _ error: any Errorable) {
self.label = label self.label = label
self.error = error self.error = error
-116
View File
@@ -1,116 +0,0 @@
// p4rse, Copyright 2026, Will Hawkins
//
// This file is part of p4rse.
//
// This file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
public enum StyleColor {
case Red
case Blue
case Green
}
public enum StyleFormat {
case Underline
case Bold
}
public struct Style: Equatable {
let color: StyleColor?
let format: [StyleFormat]
public init(_ color: StyleColor?, _ format: [StyleFormat] = []) {
self.color = color
self.format = format
}
public func update(setColor color: StyleColor) -> Style {
return Style(color, self.format)
}
public func removeColor() -> Style {
return Style(nil, self.format)
}
public func update(addFormat format: StyleFormat) -> Style {
return if self.format.contains(format) {
Style(self.color, self.format)
} else {
Style(self.color, self.format + [format])
}
}
public func update(removeFormat format: StyleFormat) -> Style {
let new_format = self.format.filter { existing_format in
existing_format != format
}
return Style(self.color, new_format)
}
public func getColor() -> StyleColor? {
return self.color
}
public func getFormat() -> [StyleFormat] {
return self.format
}
}
public struct FormatterPlain: Formattable {
public init() {}
public func formatWithStyle(_ value: String, _ style: Style) -> String {
return value
}
}
public struct FormatterAnsi: Formattable {
public init() {}
let startFormat: String = "\u{1B}["
let resetFormat: String = "\u{1B}[0m"
let colorMap = [
StyleColor.Red: "31",
StyleColor.Green: "32",
StyleColor.Blue: "34",
]
let styleMap = [
StyleFormat.Underline: "4",
StyleFormat.Bold: "1",
]
public func formatWithStyle(_ value: String, _ style: Style) -> String {
let color =
if let color = style.getColor() {
self.colorMap[color]!
} else {
""
}
let style = style.getFormat().map { format in
String(self.styleMap[format]!)
}.joined(separator: ";")
if color.isEmpty && style.isEmpty {
return value
}
let code = startFormat + color + ((!color.isEmpty && !style.isEmpty) ? ";" : "") + style + "m"
return code + value + resetFormat
}
}
+1 -5
View File
@@ -71,7 +71,7 @@ public protocol ProgramExecutionEvaluator {
} }
public protocol Errorable: CustomStringConvertible { public protocol Errorable: CustomStringConvertible {
func format(_ formatter: Formattable) -> String func format() -> String
func msg() -> String func msg() -> String
func append(error: any Errorable) -> any Errorable func append(error: any Errorable) -> any Errorable
func eq(_ rhs: any Errorable) -> Bool func eq(_ rhs: any Errorable) -> Bool
@@ -83,10 +83,6 @@ extension Errorable {
} }
} }
public protocol Formattable {
func formatWithStyle(_ value: String, _ style: Style) -> String
}
extension ProgramExecutionEvaluator { extension ProgramExecutionEvaluator {
public func ExecuteStatements( public func ExecuteStatements(
_ statements: [EvaluatableStatement], inExecution execution: ProgramExecution _ statements: [EvaluatableStatement], inExecution execution: ProgramExecution
+1 -1
View File
@@ -62,7 +62,7 @@ extension Result: CustomStringConvertible where OKT: CustomStringConvertible {
public var description: String { public var description: String {
switch self { switch self {
case Result.Error(let e): case Result.Error(let e):
return e.msg() return e.format()
case Result.Ok(let o): case Result.Ok(let o):
return "Ok: \(o)" return "Ok: \(o)"
} }
+16 -19
View File
@@ -157,7 +157,7 @@ public struct Parser {
sourceLocation: node.toSourceLocation(), withError: "Did not find expected statements")) sourceLocation: node.toSourceLocation(), withError: "Did not find expected statements"))
} }
var errors: (any Errorable)? = .none var parse_errs: [any Errorable] = Array()
var current_context = context var current_context = context
var parsed_s: [EvaluatableStatement] = Array() var parsed_s: [EvaluatableStatement] = Array()
@@ -169,19 +169,17 @@ public struct Parser {
current_context = updated_context current_context = updated_context
parsed_s.append(parsed_statement) parsed_s.append(parsed_statement)
case .Error(let e): case .Error(let e):
errors = parse_errs.append(e)
if let errors = errors {
errors.append(error: e)
} else {
e
}
} }
} }
if let errors = errors { if !parse_errs.isEmpty {
return .Error(errors) return Result.Error(
Error(
withMessage: parse_errs.map { err in
return String(err.format())
}.joined(separator: ";")))
} }
return Result.Ok((parsed_s, current_context)) return Result.Ok((parsed_s, current_context))
} }
} }
@@ -244,7 +242,7 @@ public struct Parser {
sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration") sourceLocation: node.toSourceLocation(), withError: "Missing body of state declaration")
)) ))
var errors: (any Errorable)? = .none var parse_errs: [any Errorable] = Array()
var current_context = context var current_context = context
var parsed_s: [EvaluatableStatement] = Array() var parsed_s: [EvaluatableStatement] = Array()
@@ -256,18 +254,17 @@ public struct Parser {
parsed_s = state_statements parsed_s = state_statements
current_context = updated_context current_context = updated_context
case .Error(let error): case .Error(let error):
errors = parse_errs.append(error)
if let errors = errors {
errors.append(error: error)
} else {
error
}
} }
walker.next() walker.next()
} }
if let errors = errors { if !parse_errs.isEmpty {
return .Error(errors) return Result.Error(
Error(
withMessage: parse_errs.map { err in
return String(err.format())
}.joined(separator: ";")))
} }
#MustOr( #MustOr(
+13 -20
View File
@@ -60,7 +60,7 @@ public struct Program {
// Add our FFIs // Add our FFIs
compilation_context = compilation_context.update(newFFIs: ffis) compilation_context = compilation_context.update(newFFIs: ffis)
var errors: (any Errorable)? = .none var errors: [any Errorable] = Array()
// If the caller gave any global instances, add them here. // If the caller gave any global instances, add them here.
if let globalInstances = globalInstances { if let globalInstances = globalInstances {
@@ -90,34 +90,27 @@ public struct Program {
break break
case .Error(let e): case .Error(let e):
found_parser = true found_parser = true
errors = errors.append(e)
if let errors = errors {
errors.append(error: e)
} else {
e
}
break break
} }
} }
// If none of the declaration parsers chose to parse, that's an error, too! // If none of the declaration parsers chose to parse, that's an error, too!
if !found_parser { if !found_parser {
errors.append(
let no_parser_error = ErrorWithLocation( ErrorWithLocation(
sourceLocation: specific_declaration_node.toSourceLocation(), sourceLocation: specific_declaration_node.toSourceLocation(),
withError: "Could not find parser for declaration node" withError: "Could not find parser for declaration node"
) ))
errors =
if let errors = errors {
errors.append(error: no_parser_error)
} else {
no_parser_error
}
} }
} }
if let errors = errors { if !errors.isEmpty {
return .Error(errors) return Result.Error(
Error(
withMessage: errors.map { error in
return error.format()
}.joined(separator: ";")))
} }
// Any of the instances that are in the top-level scope should go into the program! // Any of the instances that are in the top-level scope should go into the program!
+4 -28
View File
@@ -18,8 +18,8 @@
import Common import Common
import Foundation import Foundation
import Macros import Macros
import P4Lang
import P4Runtime import P4Runtime
import P4Lang
import SwiftTreeSitter import SwiftTreeSitter
import Testing import Testing
import TreeSitter import TreeSitter
@@ -28,31 +28,7 @@ import TreeSitterP4
@testable import P4Compiler @testable import P4Compiler
@Test func test_error_with_location_formatting() async throws { @Test func test_error_with_location_formatting() async throws {
let formatter = FormatterAnsi() let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "There was an error")
let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "There was an error")
let formatted = e.format(formatter) print(e.format())
#expect(formatted == "\u{1B}[31;1m{1, 5}\u{1B}[0m: There was an error")
}
@Test func test_errors_with_location_no_formatting() async throws {
let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "There was an error")
let e1 = ErrorWithLocation(
sourceLocation: SourceLocation(10, 5), withError: "There was another error")
let formatted = e.append(error: e1).format(FormatterPlain())
#expect(formatted == "{1, 5}: There was an error\n{10, 5}: There was another error")
}
@Test func test_errors_with_location_ansi_formatting() async throws {
let e = ErrorWithLocation(sourceLocation: SourceLocation(1, 5), withError: "There was an error")
let e1 = ErrorWithLocation(
sourceLocation: SourceLocation(10, 5), withError: "There was another error")
let formatted = e.append(error: e1).format(FormatterAnsi())
#expect(
formatted
== "\u{1B}[31;1m{1, 5}\u{1B}[0m: There was an error\n\u{1B}[31;1m{10, 5}\u{1B}[0m: There was another error"
)
} }
-57
View File
@@ -1,57 +0,0 @@
// p4rse, Copyright 2026, Will Hawkins
//
// This file is part of p4rse.
//
// This file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import Common
import Foundation
import Macros
import P4Runtime
import P4Lang
import SwiftTreeSitter
import Testing
import TreeSitter
import TreeSitterP4
@testable import P4Compiler
@Test func test_style_add_format() async throws {
let red = Style(StyleColor.Red)
let bold_red = red.update(addFormat: StyleFormat.Bold)
#expect(bold_red == Style(StyleColor.Red, [StyleFormat.Bold]))
}
@Test func test_style_add_format2() async throws {
let bold_red = Style(StyleColor.Red, [StyleFormat.Bold])
let bold_underline_red = bold_red.update(addFormat: StyleFormat.Underline)
#expect(bold_underline_red == Style(StyleColor.Red, [StyleFormat.Bold, StyleFormat.Underline]))
}
@Test func test_style_remove_format() async throws {
let bold_red = Style(StyleColor.Red, [StyleFormat.Bold])
let red = bold_red.update(removeFormat: StyleFormat.Bold)
#expect(red == Style(StyleColor.Red))
}
@Test func test_style_remove_format2() async throws {
let bold_underline_red = Style(StyleColor.Red, [StyleFormat.Bold, StyleFormat.Underline])
let underline_red = bold_underline_red.update(removeFormat: StyleFormat.Bold)
#expect(underline_red == Style(StyleColor.Red, [StyleFormat.Underline]))
}
+1 -1
View File
@@ -42,7 +42,7 @@ import TreeSitterP4
guard case Result.Error(let e) = err else { guard case Result.Error(let e) = err else {
assert(false, "Expected an error, but had success") assert(false, "Expected an error, but had success")
} }
#expect(e.msg().contains("Failed to parse a statement element: Could not parse a P4 type from \(invalid_type_name)")) #expect(e.format().contains("Failed to parse a statement element: Could not parse a P4 type from \(invalid_type_name)"))
} }
} }