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
6 changes: 6 additions & 0 deletions .githooks/check
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,10 @@ echo "Checking Stylelint for CSS files..."
if ! npm run lint:css; then
echo "Error: Stylelint check for CSS files failed." >&2
exit 1
fi

echo "Running Rust tests..."
if ! cargo test --manifest-path "$PROJECT_ROOT/rust/Cargo.toml"; then
echo "Error: Rust tests failed." >&2
exit 1
fi
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,33 @@ jobs:

- name: Check Stylelint for CSS files
run: npm run lint:css

rust:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"

- name: Install ANTLR
run: |
curl -L -o rust/antlr4rust.jar https://github.com/rrevenantt/antlr4rust/releases/download/antlr4-4.8-2-Rust0.3.0-beta/antlr4-4.8-2-SNAPSHOT-complete.jar

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Generate grammar
run: |
chmod +x ./generate_parser.sh
./generate_parser.sh
working-directory: rust

- name: Run Rust tests
run: cargo test
working-directory: rust
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ grammar/.antlr/
node_modules/

# Output files.
output/
dist/
output/

# Parsers, generated from `IconScript.g4` by ANTLR4.
grammar/*.interp
Expand All @@ -21,3 +21,12 @@ web/bundles/*.bundle.min.js.map
# WIP.
webassembly/
work/

# Rust.
**/*.rs.bk
*.long-type-*.txt
*.pdb
rust/Cargo.lock
rust/antlr4rust.jar
rust/src/grammar/
rust/target/
1 change: 1 addition & 0 deletions .stylelintignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules/
dist/
*.bundle.css
rust/target/
output/
2 changes: 2 additions & 0 deletions doc/public.moi
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ the style of the \ref {#roentgen} {Röntgen} project.
</div>
</div>
}

\include {syntax.moi}
55 changes: 55 additions & 0 deletions doc/syntax.moi
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
\2 {Syntax} {iconscript_syntax}

\3 {Global context} {iconscript_global_context}

\list
{\c {width} — stroke width.}
{\c {position} — current position of the cursor.}

\3 {Commands} {iconscript_commands}

\c {\formal {position}} is 2D coordinates in the form \c {\formal {x},\formal {y}} or \c {+\formal {x},\formal {y}}
(\c {+} means that the position is relative to the \c {position}).

\table
{{Command} {Description}}
{{\c {subtract}} {Set subtraction mode}}
{{\c {w \formal {float}}} {Set \c {width} to a value}}
{{\c {m \formal {position}}} {Set \c {position} to a value}}
{{\c {l [\formal {position}]}} {Draw lines between positions}}
{{\c {lf [\formal {position}]}} {Draw filled lines between positions}}
{
{\c {e \formal {position} \formal {float}}}
{Draw circle specified by center point and radius}
}
{
{\c {r \formal {position} \formal {position}}}
{Draw rectangle specified by top left and bottom right points}
}
{
{\c {a \formal {position} \formal {float} \formal {float} \formal {float}}}
{Draw arc specified by center point, radius, and two angles in radians}
}

\3 {Variables} {iconscript_variables}

Variables can be defined with \c {<variable> = [<command>]} and accessed with
\c {@<variable>}.

\3 {Scopes} {iconscript_scopes}

Scopes group commands together using \c {\{} and \c {\}}. They can be nested and
are used to incapsulate context changes.

\3 {Example} {iconscript_example}

\code {iconscript} {
\sv {square} = \skw {lf} +0,0 +2,0 +0,2 +-2,0 +0,-2
icon glider = \{
\skw {m} 6,2 \sv {@square} \skw {m} +4,4 \sv {@square}
\skw {m} +-8,4 \sv {@square} \skw {m} +4,0 \sv {@square} \skw {m} +4,0 \sv {@square}
\}
}

This code defines a square (\c {lf}, filled line — polygon with 5 points). It
then reuses \c {square} variable 5 times to draw a glider.
23 changes: 23 additions & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "iconscript"
version = "0.2.0"
edition = "2021"
authors = ["Sergey Vartanov"]
description = "iconscript parser and SVG generator in Rust"
license = "MIT"
repository = "https://github.com/enzet/iconscript"
readme = "README.md"
keywords = ["icons", "svg", "cli"]

[[bin]]
name = "iconscript"
path = "src/main.rs"

[dependencies]
antlr-rust = "0.3.0-beta"
anyhow = "1.0"
clap = { version = "4.5", features = ["derive"] }
kurbo = "0.12.0"
lazy_static = "1.4"
linesweeper = "0.1.2"
thiserror = "1.0"
21 changes: 21 additions & 0 deletions rust/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Sergey Vartanov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
43 changes: 43 additions & 0 deletions rust/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# iconscript implementation in Rust

- Parses iconscript files using ANTLR grammar.
- Generates SVG files using the `linesweeper` library for Boolean path
operations.

## Building

```bash
cd rust
./generate_parser.sh
cargo build --release
```

The binary will be available at `target/release/iconscript`.

## Usage

```shell
./target/release/iconscript $OPTIONS $ICONSCRIPT_FILE
```

| Option | Description |
| -------------------- | -------------------------------------------------- |
| `-o`, `--output` | Output directory for SVG files (default: `output`) |
| `-s`, `--sketch` | Output raw paths without combining |
| `--no-rounding` | Disable coordinate rounding |
| `--no-deduplication` | Disable duplicate point removal |
| `--no-collinear` | Disable collinear point simplification |

## Testing

Run tests:

```shell
cargo test
```

Try to run on test file:

```shell
cargo run -- ../test/main.iconscript -o test-output/
```
6 changes: 6 additions & 0 deletions rust/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
// Before running this file, run `./generate_parser.sh`.

let grammar_file = "../grammar/IconScript.g4";
println!("cargo:rerun-if-changed={}", grammar_file);
}
39 changes: 39 additions & 0 deletions rust/generate_parser.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash

# Script to generate Rust parser from ANTLR grammar
# This uses the ANTLR4 Rust tool

set -e

GRAMMAR_FILE="../grammar/IconScript.g4"
OUTPUT_DIR="src/parser"
ANTLR_JAR="antlr4rust.jar"

# Create output directory
mkdir -p "$OUTPUT_DIR"

# Check if ANTLR Rust jar exists
if [ ! -f "$ANTLR_JAR" ]; then
echo "Downloading ANTLR4 Rust target..."
curl -L -o "$ANTLR_JAR" \
"https://github.com/rrevenantt/antlr4rust/releases/download/antlr4-4.8-2-Rust0.3.0-beta/antlr4-4.8-2-SNAPSHOT-complete.jar"

if [ $? -ne 0 ]; then
echo "ERROR: Failed to download ANTLR Rust jar."
echo "Please download manually from:"
echo " https://github.com/rrevenantt/antlr4rust/releases"
exit 1
fi
fi

# Generate parser
echo "Generating Rust parser from $GRAMMAR_FILE..."
java -jar "$ANTLR_JAR" -Dlanguage=Rust -visitor -o src/grammar "$GRAMMAR_FILE"

# Move generated files to correct location
echo "Moving generated files to $OUTPUT_DIR..."
mv src/grammar/iconscript*.rs "$OUTPUT_DIR/" 2>/dev/null || true

echo "Parser generated successfully!"
echo "Generated files:"
ls -lh "$OUTPUT_DIR"/*.rs
1 change: 1 addition & 0 deletions rust/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
max_width = 80
Loading