Skip to content

Commit 2339c5c

Browse files
committed
integrated the parser into the actual service provided
1 parent 181c079 commit 2339c5c

File tree

8 files changed

+222
-18
lines changed

8 files changed

+222
-18
lines changed

conversion2025/testing.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,13 @@
8282
"source": [
8383
"import requests\n",
8484
"\n",
85-
"data = \"x^2 + y^2 = z^2\"\n",
85+
"data = \"$$x^2 + y^2$$ = z^2\"\n",
8686
"resp = requests.post(\"http://localhost:8080/process\", data=data)\n",
8787
"\n",
8888
"if resp.ok:\n",
8989
" print(\"✅ Processed:\", resp.text)\n",
9090
"else:\n",
91-
" print(\"❌ Error:\", resp.status_code)"
91+
" print(\"❌ Error:\", resp.text)"
9292
]
9393
}
9494
],
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# Math Delimiter Validator
2+
3+
A Scala-based validator for mathematical expressions in Markdown format. This tool validates the correct usage of single dollar signs (`$`) for inline math and double dollar signs (`$$`) for display math expressions.
4+
5+
## Features
6+
7+
- **Syntax Validation**: Parses Markdown content to ensure proper math delimiter pairing
8+
- **Semantic Validation**: Enforces rules like no newlines in inline math expressions
9+
- **HTTP Server**: REST API endpoint for validation services
10+
- **Markdown Reconstruction**: Ability to rebuild valid Markdown from parsed structure
11+
12+
## Requirements
13+
14+
- Scala 3.3.1
15+
- sbt 1.11.3
16+
- Java 8 or higher
17+
18+
## Installation
19+
20+
Clone the repository and navigate to the validator directory:
21+
22+
```bash
23+
cd /home/jimbo/lambda/in2lambda/conversion2025/tools/validator
24+
```
25+
26+
## Usage
27+
28+
### Building the Project
29+
30+
```bash
31+
sbt compile
32+
```
33+
34+
### Running Tests
35+
36+
```bash
37+
sbt test
38+
```
39+
40+
### Starting the HTTP Server
41+
42+
```bash
43+
sbt run
44+
```
45+
46+
The server will start on `localhost:8080` with a POST endpoint at `/process`.
47+
48+
### Programmatic Usage
49+
50+
```scala
51+
import parser.*
52+
import Block.*
53+
54+
// Parse markdown content
55+
val result = parser.parse("Hello $x + y = z$ world!")
56+
result match {
57+
case Right(markdown) =>
58+
// Validate the parsed content
59+
if (validate_markdown(markdown)) {
60+
println("Valid markdown!")
61+
// Rebuild if needed
62+
val rebuilt = rebuild_markdown(markdown)
63+
} else {
64+
println("Invalid markdown content")
65+
}
66+
case Left(error) =>
67+
println(s"Parse error: $error")
68+
}
69+
```
70+
71+
## Validation Rules
72+
73+
### Inline Math (`$...$`)
74+
- Must be enclosed by single dollar signs
75+
- Cannot contain newline characters
76+
- Example: `$x + y = z$`
77+
78+
### Display Math (`$$...$$`)
79+
- Must be enclosed by double dollar signs
80+
- Can contain newline characters
81+
- Should be on separate lines for proper formatting
82+
- Example:
83+
```
84+
$$
85+
E = mc^2
86+
$$
87+
```
88+
89+
### Text
90+
- Regular markdown text outside of math expressions
91+
- Dollar signs can be escaped with backslash (`\$`)
92+
93+
## Project Structure
94+
95+
```
96+
src/
97+
├── main/scala/
98+
│ ├── Main.scala # HTTP server entry point
99+
│ ├── parser.scala # Main parsing logic using Parsley
100+
│ ├── lexer.scala # Lexical analysis definitions
101+
│ ├── syntax.scala # AST definitions for markdown blocks
102+
│ └── semantic.scala # Validation rules and markdown reconstruction
103+
└── test/scala/
104+
└── MainTest.scala # Comprehensive test suite
105+
```
106+
107+
## Dependencies
108+
109+
- **Parsley**: Parser combinator library for syntax analysis
110+
- **http4s**: HTTP server framework
111+
- **cats-effect**: Functional effects library
112+
- **munit**: Testing framework
113+
114+
## API
115+
116+
### POST /process
117+
118+
Processes markdown content and returns validation results.
119+
120+
**Request:**
121+
```
122+
Content-Type: text/plain
123+
Body: Raw markdown content
124+
```
125+
126+
**Response:**
127+
```
128+
Content-Type: text/plain
129+
Body: Processed result
130+
```
131+
132+
## Development
133+
134+
### Adding New Validation Rules
135+
136+
1. Define new error cases in `syntax.scala`
137+
2. Implement validation logic in `semantic.scala`
138+
3. Add corresponding tests in `MainTest.scala`
139+
140+
### Extending the Parser
141+
142+
Modify `parser.scala` and `lexer.scala` to support additional markdown constructs while maintaining compatibility with existing math delimiter rules.
143+
144+
## Testing
145+
146+
The test suite covers:
147+
- Valid and invalid syntax parsing
148+
- Semantic validation rules
149+
- Markdown reconstruction
150+
- Edge cases and error conditions
151+
152+
Run specific test suites:
153+
```bash
154+
sbt "testOnly MainSpec"
155+
```
156+
157+
## License
158+
159+
This project is part of the in2lambda conversion tools.

conversion2025/tools/validator/src/main/scala/Main.scala

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
1+
package validator
2+
13
import cats.effect._
2-
import org.http4s._
4+
import org.http4s.{parser => _, _}
35
import org.http4s.dsl.io._
46
import org.http4s.ember.server.EmberServerBuilder
57
import org.http4s.server.Router
68
import com.comcast.ip4s.{Host, Port}
79

10+
import parser.*
11+
import validator.{validate_markdown, rebuild_markdown}
12+
813
object Main extends IOApp {
914

1015
val mathRoutes = HttpRoutes.of[IO] {
1116
case req @ POST -> Root / "process" =>
12-
req.as[String].flatMap { input =>
13-
val cleaned = input.trim.reverse // Example logic
14-
Ok(s"Processed: $cleaned")
17+
req.as[String].flatMap { input =>
18+
parser.parse(input) match {
19+
case Right(markdown) =>
20+
if (validate_markdown(markdown)) {
21+
val cleaned = rebuild_markdown(markdown)
22+
Ok(s"Processed: $cleaned")
23+
} else {
24+
BadRequest("Invalid Markdown: validation failed")
25+
}
26+
case Left(error) =>
27+
BadRequest(s"Parse error: ${error.toString}")
28+
}
1529
}
1630
}
1731

conversion2025/tools/validator/src/main/scala/lexer.scala

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1+
package validator
2+
13
import parsley.Parsley
2-
import parsley.token.{Lexer, Basic}
4+
import parsley.token.{Lexer, Basic, Unicode}
35
import parsley.token.descriptions.*
46
import parsley.errors.combinator.*
57

68
// Define the lexer with a custom lexical description
79
object lexer {
810

911
// Define the set of generic keywords
10-
private val genericKeywords = Set(
12+
private val genericOperators = Set(
1113
"$$", "$"
1214
)
1315

16+
// private val genericKeywords = Set(
17+
// "\\$"
18+
// )
19+
1420
// Define the lexical description
1521
private val desc = LexicalDesc.plain.copy(
1622
// Define how identifiers and symbols are recognized
@@ -21,8 +27,16 @@ object lexer {
2127

2228
// Define how symbols are recognized
2329
symbolDesc = SymbolDesc.plain.copy(
24-
hardOperators = genericKeywords
25-
)
30+
hardOperators = genericOperators,
31+
// hardKeywords = genericKeywords
32+
),
33+
34+
// textDesc = TextDesc.plain.copy(
35+
// escapeSequences = EscapeDesc.plain.copy(
36+
// escBegin = '\\',
37+
// literals = Set('$')
38+
// )
39+
// )
2640
)
2741

2842
private val lexer = Lexer(desc)

conversion2025/tools/validator/src/main/scala/parser.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package validator
2+
13
import parsley.quick.*
24
import parsley.syntax.zipped.*
35
import parsley.expr.chain
@@ -7,10 +9,8 @@ import parsley.errors.combinator.*
79

810
import lexer.{fully, text}
911
import lexer.implicits.implicitSymbol
10-
1112
import Block.*
1213

13-
1414
object parser {
1515
def parse[Err: ErrorBuilder](input: String): Either[Err, MarkDown] = parser.parse(input).toEither
1616

conversion2025/tools/validator/src/main/scala/semantic.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import Block.*
1+
package validator
2+
import Block.*
23

34
// check that the inline math does not contain newlines
45
def inline_check(math: String): Boolean = !math.contains('\n')

conversion2025/tools/validator/src/main/scala/syntax.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
package validator
12
import parsley.generic
23

34
// This file defines the lexer for parsing mathematical expressions in Markdown format.

conversion2025/tools/validator/src/test/scala/MainTest.scala

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@ class MainSpec extends FunSuite {
3535
assertEquals(result, Right(MarkDown(List(Display("x+y=z")))))
3636
}
3737

38+
test("display math with newlines") {
39+
val result = parser.parse("$$\nE = \nmc^2\n$$")
40+
assertEquals(result, Right(MarkDown(List(Display("E = \nmc^2\n")))))
41+
}
42+
43+
// test ("allow escaped dollar signs in inline math") {
44+
// val result = parser.parse("$ x + \\$y = z $")
45+
// assertEquals(result, Right(MarkDown(List(Inline("x + $y = z ")))))
46+
// }
47+
48+
// test ("allow escaped dollar signs in display math") {
49+
// val result = parser.parse("$$ x + \\$y = z $$")
50+
// assertEquals(result, Right(MarkDown(List(Display("x + $y = z ")))))
51+
// }
52+
3853

3954
// invalid tests
4055
test("missing closing inline math delimiter") {
@@ -84,16 +99,16 @@ class MainSpec extends FunSuite {
8499
assert(validate_markdown(markdown))
85100
}
86101

102+
test("allows display math with newlines") {
103+
val markdown = MarkDown(List(Display("E = mc^2\n")))
104+
assert(validate_markdown(markdown))
105+
}
106+
87107
// rebuilding markdown
88108
test("rebuild markdown from parsed structure") {
89109
val markdown = MarkDown(List(Text("Hello"), Inline("x + y"), Display("E = mc^2")))
90110
val rebuilt = rebuild_markdown(markdown)
91111
assertEquals(rebuilt, "Hello$x + y$\n$$\nE = mc^2\n$$\n")
92112
}
93113

94-
test("invalid thing") {
95-
val string = "Hello world$!"
96-
val result = parser.parse(string)
97-
assertEquals(result, Left("yikes"))
98-
}
99114
}

0 commit comments

Comments
 (0)