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
65 changes: 36 additions & 29 deletions .githooks/check
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
JS_DIR="$PROJECT_ROOT/js"

if ! command -v antlr4 &> /dev/null && ! command -v antlr &> /dev/null; then
echo "Error: ANTLR is not installed or not in PATH." >&2
Expand All @@ -13,58 +14,64 @@ echo "Cleaning up..."
find "$PROJECT_ROOT/grammar" \
-type f \( -name "*.ts" -o -name "*.interp" -o -name "*.tokens" \) \
-delete
rm -f "$PROJECT_ROOT/web/bundles/parser.bundle.js"
rm -f "$PROJECT_ROOT/web/bundles/parser.bundle.min.js"
rm -f "$PROJECT_ROOT/web/bundles/parser.bundle.min.js.map"
rm -f "$PROJECT_ROOT/web/bundles/ui.bundle.js"
rm -f "$PROJECT_ROOT/web/bundles/ui.bundle.min.js"
rm -f "$PROJECT_ROOT/web/bundles/ui.bundle.min.js.map"
rm -rf "$PROJECT_ROOT/output"
rm -f "$JS_DIR/web/bundles/parser.bundle.js"
rm -f "$JS_DIR/web/bundles/parser.bundle.min.js"
rm -f "$JS_DIR/web/bundles/parser.bundle.min.js.map"
rm -f "$JS_DIR/web/bundles/ui.bundle.js"
rm -f "$JS_DIR/web/bundles/ui.bundle.min.js"
rm -f "$JS_DIR/web/bundles/ui.bundle.min.js.map"
rm -rf "$JS_DIR/output"

echo "Generating grammar..."
"$PROJECT_ROOT/scripts/generate-grammar.sh"
"$JS_DIR/scripts/generate-grammar.sh"
if [ ! -f "$PROJECT_ROOT/grammar/IconScriptLexer.ts" ]; then
echo "Error: grammar was not created." >&2
exit 1
fi

cd "$JS_DIR" || exit 1

# Create grammar symlink if it doesn't exist.
if [ ! -L "$JS_DIR/grammar" ]; then
ln -s ../grammar "$JS_DIR/grammar"
fi

echo "Building parser..."
npm run build:parser:min
# Check that `parser.bundle.min.js` was created.
if [ ! -f "$PROJECT_ROOT/web/bundles/parser.bundle.min.js" ]; then
if [ ! -f "$JS_DIR/web/bundles/parser.bundle.min.js" ]; then
echo "Error: parser.bundle.min.js was not created." >&2
exit 1
fi

echo "Creating SVG icons..."
mkdir -p "$PROJECT_ROOT/output"
mkdir -p "$PROJECT_ROOT/output/main"
npm run generate -- \
"$PROJECT_ROOT/test/main.iconscript" \
"$PROJECT_ROOT/output/main" &> /dev/null
if [ ! -d "$PROJECT_ROOT/output" ]; then
echo "Error: output directory was not created." >&2
echo "Building command-line interface..."
rm -rf "$JS_DIR/dist"
npm run build:cli
# Check that CLI was created.
if [ ! -f "$JS_DIR/dist/cli/generate-svg.js" ]; then
echo "Error: dist/cli/generate-svg.js was not created." >&2
exit 1
fi
if [ ! -s "$PROJECT_ROOT/output" ]; then
echo "Error: output directory is empty." >&2

echo "Creating SVG icons (testing CLI)..."
mkdir -p "$JS_DIR/output"
mkdir -p "$JS_DIR/output/main"
if ! npm run generate -- \
"$JS_DIR/test/main.iconscript" \
"$JS_DIR/output/main"; then
echo "Error: SVG icon generation failed." >&2
exit 1
fi

echo "Creating command-line interface..."
rm -rf "$PROJECT_ROOT/dist"
npm run build:cli
# Check that `iconscript` was created.
if [ ! -f "$PROJECT_ROOT/dist/cli/generate-svg.js" ]; then
echo "Error: iconscript was not created." >&2
if [ ! -d "$JS_DIR/output/main" ] || [ -z "$(ls -A "$JS_DIR/output/main")" ]; then
echo "Error: output directory was not created or is empty." >&2
exit 1
fi

echo "Building library..."
rm -rf "$PROJECT_ROOT/dist"
rm -rf "$JS_DIR/dist"
npm run build:lib
# Check that `dist/` was created.
if [ ! -f "$PROJECT_ROOT/dist/src/parser.js" ]; then
if [ ! -f "$JS_DIR/dist/src/parser.js" ]; then
echo "Error: dist/src/parser.js was not created." >&2
exit 1
fi
Expand Down Expand Up @@ -98,4 +105,4 @@ echo "Running Rust tests..."
if ! cargo test --manifest-path "$PROJECT_ROOT/rust/Cargo.toml"; then
echo "Error: Rust tests failed." >&2
exit 1
fi
fi
42 changes: 26 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ on:
branches: [main, master]

jobs:
check:
js:
runs-on: ubuntu-latest
defaults:
run:
working-directory: js

steps:
- name: Checkout code
Expand All @@ -19,6 +22,7 @@ jobs:
with:
node-version: "20"
cache: "npm"
cache-dependency-path: js/package-lock.json

- name: Setup Java
uses: actions/setup-java@v4
Expand All @@ -27,6 +31,7 @@ jobs:
java-version: "17"

- name: Install ANTLR
working-directory: .
run: |
wget -q https://www.antlr.org/download/antlr-4.13.2-complete.jar -O /tmp/antlr.jar
echo '#!/bin/bash' > /tmp/antlr4
Expand All @@ -37,19 +42,24 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Create grammar symlink
run: ln -s ../grammar grammar

- name: Clean up generated files
working-directory: .
run: |
find grammar -type f \( -name "*.ts" -o -name "*.interp" -o -name "*.tokens" \) -delete || true
rm -f web/bundles/parser.bundle.js web/bundles/parser.bundle.min.js web/bundles/parser.bundle.min.js.map
rm -f web/bundles/ui.bundle.js web/bundles/ui.bundle.min.js web/bundles/ui.bundle.min.js.map
rm -rf output
rm -f js/web/bundles/parser.bundle.js js/web/bundles/parser.bundle.min.js js/web/bundles/parser.bundle.min.js.map
rm -f js/web/bundles/ui.bundle.js js/web/bundles/ui.bundle.min.js js/web/bundles/ui.bundle.min.js.map
rm -rf js/output

- name: Generate grammar
run: |
chmod +x ./scripts/generate-grammar.sh
./scripts/generate-grammar.sh

- name: Verify grammar generation
working-directory: .
run: |
if [ ! -f "grammar/IconScriptLexer.ts" ]; then
echo "Error: grammar was not created."
Expand All @@ -66,18 +76,6 @@ jobs:
exit 1
fi

- name: Create SVG icons (test)
run: |
mkdir -p output/main
npm run generate -- test/main.iconscript output/main

- name: Verify SVG icons creation
run: |
if [ ! -d "output" ] || [ -z "$(ls -A output)" ]; then
echo "Error: output directory was not created or is empty."
exit 1
fi

- name: Build CLI
run: |
rm -rf dist
Expand All @@ -90,6 +88,18 @@ jobs:
exit 1
fi

- name: Create SVG icons (test)
run: |
mkdir -p output/main
npm run generate -- test/main.iconscript output/main

- name: Verify SVG icons creation
run: |
if [ ! -d "output/main" ] || [ -z "$(ls -A output/main)" ]; then
echo "Error: output directory was not created or is empty."
exit 1
fi

- name: Build library
run: |
rm -rf dist
Expand Down
17 changes: 9 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
*.egg-info/
.npmrc
.env
__pycache__/
grammar/.antlr/
node_modules/

# Output files.
dist/
output/
# JavaScript implementation.
js/.npmrc
js/node_modules/
js/dist/
js/output/

# Parsers, generated from `IconScript.g4` by ANTLR4.
grammar/*.interp
grammar/*.tokens
grammar/*.ts

# Bundled JavaScript files.
web/bundles/*.bundle.js
web/bundles/*.bundle.min.js
web/bundles/*.bundle.min.js.map
js/web/bundles/*.bundle.js
js/web/bundles/*.bundle.min.js
js/web/bundles/*.bundle.min.js.map

# WIP.
webassembly/
Expand Down
97 changes: 28 additions & 69 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,83 +7,42 @@ project.
The grammar of the language is described in the ANTLR4 `grammar/IconScript.g4`
file.

## Installation

Install dependencies:

```shell
npm install
```

## Development Setup

Generate grammar files:

```shell
npm run generate:grammar
```

Build all components (library, CLI, and web bundles):

```shell
npm run build
```

Build individually:
- `npm run generate:grammar` — generate grammar files,
- `npm run build:lib` — build TypeScript library,
- `npm run build:cli` — build CLI tool,
- `npm run build:parser:min` — build parser bundle for web,
- `npm run build:ui:min` — build UI bundle for web.

## Usage

### Command-Line Interface

```shell
npm run generate $INPUT_ICONSCRIPT_FILE $OUTPUT_DIR
```

### Web Interface

1. Build the web bundles (if not already built):

```shell
npm run build:parser:min
npm run build:ui:min
```

2. Start a local server (e.g., using `live-server`):

```shell
npm install -g live-server
live-server web
```

Or use any other static file server pointing to the `web/` directory.
## SVG generation

There are two implementations of iconscript for parsing and generating SVG
files.
- __Rust__: `cargo install iconscript`. Rust implementation is _faster_ and
_more reliable_. It uses
[`linesweeper`](https://docs.rs/linesweeper/latest/linesweeper/) library for
Boolean path operations and SVG optimizations.
- __JavaScript__ (TypeScript): `npm install iconscript`. JavaScript
implementation uses [Paper.js](http://paperjs.org/) library, that may produce
wrong outputs.

## Syntax

Syntax slightly resembles the syntax of path commands in SVG.

### Global context

- __width__ — stroke width.
- __position__ — current position of the cursor.
- `width` — stroke width.
- `position` — current position of the cursor.

### Commands

`<position>` is 2D coordinates in the form `<x>,<y>` or `+<x>,<y>` (`+` means
`<vector>` is 2D coordinates in the form `<x>,<y>` or `+<x>,<y>` (`+` means
that the position is relative to the __position__).

| Command | Description |
|---|---|
| `subtract` | Set subtraction mode |
| `w <float>` | Set __width__ to a value |
| `m <position>` | Set __position__ to a value |
| `l [<position>]` | Draw lines between positions |
| `lf [<position>]` | Draw filled lines between positions |
| `e <position> <float>` | Draw circle specified by center point and radius |
| `r <position> <position>` | Draw rectangle specified by top left and bottom right points |
| `a <position> <float> <float> <float>` | Draw arc specified by center point, radius, and two angles in radians |
| Command | Description |
| ------------------------------------ | --------------------------------------------------------------------------------- |
| `subtract` | Set subtraction mode |
| `w <float>` | Set `width` to a value |
| `m <vector>` | Set `position` to a value |
| `l [<vector>]` | Draw lines between positions |
| `lf [<vector>]` | Draw filled lines between positions |
| `e <vector> <float>` | Draw circle specified by center point and radius |
| `r <vector> <vector>` | Draw rectangle specified by top left and bottom right points |
| `a <vector> <float> <float> <float>` | Draw arc specified by center point, radius, start angle, and end angle in radians |

### Variables

Expand All @@ -92,8 +51,8 @@ Variables can be defined with `<variable> = [<command>]` and accessed with

### Scopes

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

### Example

Expand Down
3 changes: 3 additions & 0 deletions js/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist/
node_modules/
web/bundles/
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading