From dd7a1e5f60f58b8f18d0e0f6cc729508639c5f85 Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Thu, 22 Jan 2026 01:52:20 +0400 Subject: [PATCH 1/7] [#9] Add `fill` command --- grammar/IconScript.g4 | 4 +- js/src/parser.ts | 12 +- rust/src/listener.rs | 8 +- rust/src/parser/iconscriptlexer.rs | 143 +++++----- rust/src/parser/iconscriptlistener.rs | 10 + rust/src/parser/iconscriptparser.rs | 374 +++++++++++++++++--------- rust/src/parser/iconscriptvisitor.rs | 19 ++ rust/src/types.rs | 2 + 8 files changed, 369 insertions(+), 203 deletions(-) diff --git a/grammar/IconScript.g4 b/grammar/IconScript.g4 index 511ee6f..284a923 100644 --- a/grammar/IconScript.g4 +++ b/grammar/IconScript.g4 @@ -28,7 +28,8 @@ command | rectangle | setPosition | setRemove - | setWidth ; + | setWidth + | setFill ; // Figures. arc : 'a' position FLOAT FLOAT FLOAT ; @@ -43,3 +44,4 @@ name : IDENTIFIER ; setPosition : 'm' position ; setWidth : 'w' FLOAT ; setRemove : 'subtract' ; +setFill : 'fill' ; diff --git a/js/src/parser.ts b/js/src/parser.ts index 7e4fab3..e48cb31 100644 --- a/js/src/parser.ts +++ b/js/src/parser.ts @@ -23,6 +23,7 @@ import type { SetPositionContext, SetWidthContext, SetRemoveContext, + SetFillContext, CommandContext, ScopeContext, PositionContext, @@ -181,15 +182,18 @@ class Scope { uniting: boolean; width: number; position: Point; + isFilled: boolean; constructor( uniting = true, width = defaultWidth, position = new Point(0, 0), + isFilled = false, ) { this.uniting = uniting; this.width = width; this.position = position; + this.isFilled = isFilled; } getPosition(pos: PositionContext): Point { @@ -212,6 +216,7 @@ class Scope { this.uniting, this.width, new Point(this.position.x, this.position.y), + this.isFilled, ); } } @@ -336,7 +341,8 @@ class IconScriptListener extends GeneratedIconScriptListener { }; exitLine = (ctx: LineContext): void => { - const isFilled = ctx.getText().includes("lf"); + const isFilled = + ctx.getText().includes("lf") || this.getScope().isFilled; const positions = ctx.position_list(); const coordinates: Point[] = []; @@ -542,6 +548,10 @@ class IconScriptListener extends GeneratedIconScriptListener { this.getScope().uniting = false; }; + exitSetFill = (_ctx: SetFillContext): void => { + this.getScope().isFilled = true; + }; + enterCommand = (ctx: CommandContext): void => { if (ctx.VARIABLE()) { // Variable reference - expand it by walking its parse tree. diff --git a/rust/src/listener.rs b/rust/src/listener.rs index bf5030a..c7f820d 100644 --- a/rust/src/listener.rs +++ b/rust/src/listener.rs @@ -154,6 +154,8 @@ impl<'input> IconScriptListenerImpl<'input> { self.exit_setWidth(&width_ctx); } else if let Some(remove_ctx) = ctx.setRemove() { self.exit_setRemove(&remove_ctx); + } else if let Some(fill_ctx) = ctx.setFill() { + self.exit_setFill(&fill_ctx); } else if let Some(name_ctx) = ctx.name() { self.exit_name(&name_ctx); } @@ -232,7 +234,7 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { } fn exit_line(&mut self, ctx: &LineContext<'input>) { - let is_filled = ctx.get_text().contains("lf"); + let is_filled = ctx.get_text().contains("lf") || self.get_scope().is_filled; let positions: Vec = ctx .position_all() .iter() @@ -483,4 +485,8 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { fn exit_setRemove(&mut self, _ctx: &SetRemoveContext<'input>) { self.get_scope_mut().uniting = false; } + + fn exit_setFill(&mut self, _ctx: &SetFillContext<'input>) { + self.get_scope_mut().is_filled = true; + } } diff --git a/rust/src/parser/iconscriptlexer.rs b/rust/src/parser/iconscriptlexer.rs index 0fdb8e0..0e77a6d 100644 --- a/rust/src/parser/iconscriptlexer.rs +++ b/rust/src/parser/iconscriptlexer.rs @@ -43,11 +43,12 @@ use std::ops::{Deref, DerefMut}; pub const T__11:isize=12; pub const T__12:isize=13; pub const T__13:isize=14; - pub const VARIABLE:isize=15; - pub const FLOAT:isize=16; - pub const IDENTIFIER:isize=17; - pub const COMMENT:isize=18; - pub const WS:isize=19; + pub const T__14:isize=15; + pub const VARIABLE:isize=16; + pub const FLOAT:isize=17; + pub const IDENTIFIER:isize=18; + pub const COMMENT:isize=19; + pub const WS:isize=20; pub const channelNames: [&'static str;0+2] = [ "DEFAULT_TOKEN_CHANNEL", "HIDDEN" ]; @@ -56,21 +57,21 @@ use std::ops::{Deref, DerefMut}; "DEFAULT_MODE" ]; - pub const ruleNames: [&'static str;19] = [ + pub const ruleNames: [&'static str;20] = [ "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8", - "T__9", "T__10", "T__11", "T__12", "T__13", "VARIABLE", "FLOAT", "IDENTIFIER", - "COMMENT", "WS" + "T__9", "T__10", "T__11", "T__12", "T__13", "T__14", "VARIABLE", "FLOAT", + "IDENTIFIER", "COMMENT", "WS" ]; - pub const _LITERAL_NAMES: [Option<&'static str>;15] = [ + pub const _LITERAL_NAMES: [Option<&'static str>;16] = [ None, Some("'+'"), Some("','"), Some("'='"), Some("'{'"), Some("'}'"), Some("'icon'"), Some("'a'"), Some("'e'"), Some("'l'"), Some("'lf'"), Some("'r'"), - Some("'m'"), Some("'w'"), Some("'subtract'") + Some("'m'"), Some("'w'"), Some("'subtract'"), Some("'fill'") ]; - pub const _SYMBOLIC_NAMES: [Option<&'static str>;20] = [ + pub const _SYMBOLIC_NAMES: [Option<&'static str>;21] = [ None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, Some("VARIABLE"), Some("FLOAT"), Some("IDENTIFIER"), + None, None, None, None, Some("VARIABLE"), Some("FLOAT"), Some("IDENTIFIER"), Some("COMMENT"), Some("WS") ]; lazy_static!{ @@ -214,63 +215,67 @@ impl<'input, Input:CharStream >> TokenSource<'input> for IconScript const _serializedATN:&'static str = "\x03\u{608b}\u{a72a}\u{8133}\u{b9ed}\u{417c}\u{3be7}\u{7786}\u{5964}\x02\ - \x15\x78\x08\x01\x04\x02\x09\x02\x04\x03\x09\x03\x04\x04\x09\x04\x04\x05\ + \x16\x7f\x08\x01\x04\x02\x09\x02\x04\x03\x09\x03\x04\x04\x09\x04\x04\x05\ \x09\x05\x04\x06\x09\x06\x04\x07\x09\x07\x04\x08\x09\x08\x04\x09\x09\x09\ \x04\x0a\x09\x0a\x04\x0b\x09\x0b\x04\x0c\x09\x0c\x04\x0d\x09\x0d\x04\x0e\ \x09\x0e\x04\x0f\x09\x0f\x04\x10\x09\x10\x04\x11\x09\x11\x04\x12\x09\x12\ - \x04\x13\x09\x13\x04\x14\x09\x14\x03\x02\x03\x02\x03\x03\x03\x03\x03\x04\ - \x03\x04\x03\x05\x03\x05\x03\x06\x03\x06\x03\x07\x03\x07\x03\x07\x03\x07\ - \x03\x07\x03\x08\x03\x08\x03\x09\x03\x09\x03\x0a\x03\x0a\x03\x0b\x03\x0b\ - \x03\x0b\x03\x0c\x03\x0c\x03\x0d\x03\x0d\x03\x0e\x03\x0e\x03\x0f\x03\x0f\ - \x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x10\x03\x10\ - \x03\x10\x03\x11\x05\x11\x55\x0a\x11\x03\x11\x06\x11\x58\x0a\x11\x0d\x11\ - \x0e\x11\x59\x03\x11\x03\x11\x07\x11\x5e\x0a\x11\x0c\x11\x0e\x11\x61\x0b\ - \x11\x05\x11\x63\x0a\x11\x03\x12\x03\x12\x07\x12\x67\x0a\x12\x0c\x12\x0e\ - \x12\x6a\x0b\x12\x03\x13\x03\x13\x07\x13\x6e\x0a\x13\x0c\x13\x0e\x13\x71\ - \x0b\x13\x03\x13\x03\x13\x03\x14\x03\x14\x03\x14\x03\x14\x02\x02\x15\x03\ - \x03\x05\x04\x07\x05\x09\x06\x0b\x07\x0d\x08\x0f\x09\x11\x0a\x13\x0b\x15\ - \x0c\x17\x0d\x19\x0e\x1b\x0f\x1d\x10\x1f\x11\x21\x12\x23\x13\x25\x14\x27\ - \x15\x03\x02\x07\x03\x02\x32\x3b\x05\x02\x43\x5c\x61\x61\x63\x7c\x06\x02\ - \x32\x3b\x43\x5c\x61\x61\x63\x7c\x04\x02\x0c\x0c\x0f\x0f\x05\x02\x0b\x0c\ - \x0f\x0f\x22\x22\x02\x7d\x02\x03\x03\x02\x02\x02\x02\x05\x03\x02\x02\x02\ - \x02\x07\x03\x02\x02\x02\x02\x09\x03\x02\x02\x02\x02\x0b\x03\x02\x02\x02\ - \x02\x0d\x03\x02\x02\x02\x02\x0f\x03\x02\x02\x02\x02\x11\x03\x02\x02\x02\ - \x02\x13\x03\x02\x02\x02\x02\x15\x03\x02\x02\x02\x02\x17\x03\x02\x02\x02\ - \x02\x19\x03\x02\x02\x02\x02\x1b\x03\x02\x02\x02\x02\x1d\x03\x02\x02\x02\ - \x02\x1f\x03\x02\x02\x02\x02\x21\x03\x02\x02\x02\x02\x23\x03\x02\x02\x02\ - \x02\x25\x03\x02\x02\x02\x02\x27\x03\x02\x02\x02\x03\x29\x03\x02\x02\x02\ - \x05\x2b\x03\x02\x02\x02\x07\x2d\x03\x02\x02\x02\x09\x2f\x03\x02\x02\x02\ - \x0b\x31\x03\x02\x02\x02\x0d\x33\x03\x02\x02\x02\x0f\x38\x03\x02\x02\x02\ - \x11\x3a\x03\x02\x02\x02\x13\x3c\x03\x02\x02\x02\x15\x3e\x03\x02\x02\x02\ - \x17\x41\x03\x02\x02\x02\x19\x43\x03\x02\x02\x02\x1b\x45\x03\x02\x02\x02\ - \x1d\x47\x03\x02\x02\x02\x1f\x50\x03\x02\x02\x02\x21\x54\x03\x02\x02\x02\ - \x23\x64\x03\x02\x02\x02\x25\x6b\x03\x02\x02\x02\x27\x74\x03\x02\x02\x02\ - \x29\x2a\x07\x2d\x02\x02\x2a\x04\x03\x02\x02\x02\x2b\x2c\x07\x2e\x02\x02\ - \x2c\x06\x03\x02\x02\x02\x2d\x2e\x07\x3f\x02\x02\x2e\x08\x03\x02\x02\x02\ - \x2f\x30\x07\x7d\x02\x02\x30\x0a\x03\x02\x02\x02\x31\x32\x07\x7f\x02\x02\ - \x32\x0c\x03\x02\x02\x02\x33\x34\x07\x6b\x02\x02\x34\x35\x07\x65\x02\x02\ - \x35\x36\x07\x71\x02\x02\x36\x37\x07\x70\x02\x02\x37\x0e\x03\x02\x02\x02\ - \x38\x39\x07\x63\x02\x02\x39\x10\x03\x02\x02\x02\x3a\x3b\x07\x67\x02\x02\ - \x3b\x12\x03\x02\x02\x02\x3c\x3d\x07\x6e\x02\x02\x3d\x14\x03\x02\x02\x02\ - \x3e\x3f\x07\x6e\x02\x02\x3f\x40\x07\x68\x02\x02\x40\x16\x03\x02\x02\x02\ - \x41\x42\x07\x74\x02\x02\x42\x18\x03\x02\x02\x02\x43\x44\x07\x6f\x02\x02\ - \x44\x1a\x03\x02\x02\x02\x45\x46\x07\x79\x02\x02\x46\x1c\x03\x02\x02\x02\ - \x47\x48\x07\x75\x02\x02\x48\x49\x07\x77\x02\x02\x49\x4a\x07\x64\x02\x02\ - \x4a\x4b\x07\x76\x02\x02\x4b\x4c\x07\x74\x02\x02\x4c\x4d\x07\x63\x02\x02\ - \x4d\x4e\x07\x65\x02\x02\x4e\x4f\x07\x76\x02\x02\x4f\x1e\x03\x02\x02\x02\ - \x50\x51\x07\x42\x02\x02\x51\x52\x05\x23\x12\x02\x52\x20\x03\x02\x02\x02\ - \x53\x55\x07\x2f\x02\x02\x54\x53\x03\x02\x02\x02\x54\x55\x03\x02\x02\x02\ - \x55\x57\x03\x02\x02\x02\x56\x58\x09\x02\x02\x02\x57\x56\x03\x02\x02\x02\ - \x58\x59\x03\x02\x02\x02\x59\x57\x03\x02\x02\x02\x59\x5a\x03\x02\x02\x02\ - \x5a\x62\x03\x02\x02\x02\x5b\x5f\x07\x30\x02\x02\x5c\x5e\x09\x02\x02\x02\ - \x5d\x5c\x03\x02\x02\x02\x5e\x61\x03\x02\x02\x02\x5f\x5d\x03\x02\x02\x02\ - \x5f\x60\x03\x02\x02\x02\x60\x63\x03\x02\x02\x02\x61\x5f\x03\x02\x02\x02\ - \x62\x5b\x03\x02\x02\x02\x62\x63\x03\x02\x02\x02\x63\x22\x03\x02\x02\x02\ - \x64\x68\x09\x03\x02\x02\x65\x67\x09\x04\x02\x02\x66\x65\x03\x02\x02\x02\ - \x67\x6a\x03\x02\x02\x02\x68\x66\x03\x02\x02\x02\x68\x69\x03\x02\x02\x02\ - \x69\x24\x03\x02\x02\x02\x6a\x68\x03\x02\x02\x02\x6b\x6f\x07\x25\x02\x02\ - \x6c\x6e\x0a\x05\x02\x02\x6d\x6c\x03\x02\x02\x02\x6e\x71\x03\x02\x02\x02\ - \x6f\x6d\x03\x02\x02\x02\x6f\x70\x03\x02\x02\x02\x70\x72\x03\x02\x02\x02\ - \x71\x6f\x03\x02\x02\x02\x72\x73\x08\x13\x02\x02\x73\x26\x03\x02\x02\x02\ - \x74\x75\x09\x06\x02\x02\x75\x76\x03\x02\x02\x02\x76\x77\x08\x14\x02\x02\ - \x77\x28\x03\x02\x02\x02\x09\x02\x54\x59\x5f\x62\x68\x6f\x03\x08\x02\x02"; + \x04\x13\x09\x13\x04\x14\x09\x14\x04\x15\x09\x15\x03\x02\x03\x02\x03\x03\ + \x03\x03\x03\x04\x03\x04\x03\x05\x03\x05\x03\x06\x03\x06\x03\x07\x03\x07\ + \x03\x07\x03\x07\x03\x07\x03\x08\x03\x08\x03\x09\x03\x09\x03\x0a\x03\x0a\ + \x03\x0b\x03\x0b\x03\x0b\x03\x0c\x03\x0c\x03\x0d\x03\x0d\x03\x0e\x03\x0e\ + \x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x0f\x03\x0f\ + \x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x11\x03\x11\x03\x11\x03\x12\ + \x05\x12\x5c\x0a\x12\x03\x12\x06\x12\x5f\x0a\x12\x0d\x12\x0e\x12\x60\x03\ + \x12\x03\x12\x07\x12\x65\x0a\x12\x0c\x12\x0e\x12\x68\x0b\x12\x05\x12\x6a\ + \x0a\x12\x03\x13\x03\x13\x07\x13\x6e\x0a\x13\x0c\x13\x0e\x13\x71\x0b\x13\ + \x03\x14\x03\x14\x07\x14\x75\x0a\x14\x0c\x14\x0e\x14\x78\x0b\x14\x03\x14\ + \x03\x14\x03\x15\x03\x15\x03\x15\x03\x15\x02\x02\x16\x03\x03\x05\x04\x07\ + \x05\x09\x06\x0b\x07\x0d\x08\x0f\x09\x11\x0a\x13\x0b\x15\x0c\x17\x0d\x19\ + \x0e\x1b\x0f\x1d\x10\x1f\x11\x21\x12\x23\x13\x25\x14\x27\x15\x29\x16\x03\ + \x02\x07\x03\x02\x32\x3b\x05\x02\x43\x5c\x61\x61\x63\x7c\x06\x02\x32\x3b\ + \x43\x5c\x61\x61\x63\x7c\x04\x02\x0c\x0c\x0f\x0f\x05\x02\x0b\x0c\x0f\x0f\ + \x22\x22\x02\u{84}\x02\x03\x03\x02\x02\x02\x02\x05\x03\x02\x02\x02\x02\ + \x07\x03\x02\x02\x02\x02\x09\x03\x02\x02\x02\x02\x0b\x03\x02\x02\x02\x02\ + \x0d\x03\x02\x02\x02\x02\x0f\x03\x02\x02\x02\x02\x11\x03\x02\x02\x02\x02\ + \x13\x03\x02\x02\x02\x02\x15\x03\x02\x02\x02\x02\x17\x03\x02\x02\x02\x02\ + \x19\x03\x02\x02\x02\x02\x1b\x03\x02\x02\x02\x02\x1d\x03\x02\x02\x02\x02\ + \x1f\x03\x02\x02\x02\x02\x21\x03\x02\x02\x02\x02\x23\x03\x02\x02\x02\x02\ + \x25\x03\x02\x02\x02\x02\x27\x03\x02\x02\x02\x02\x29\x03\x02\x02\x02\x03\ + \x2b\x03\x02\x02\x02\x05\x2d\x03\x02\x02\x02\x07\x2f\x03\x02\x02\x02\x09\ + \x31\x03\x02\x02\x02\x0b\x33\x03\x02\x02\x02\x0d\x35\x03\x02\x02\x02\x0f\ + \x3a\x03\x02\x02\x02\x11\x3c\x03\x02\x02\x02\x13\x3e\x03\x02\x02\x02\x15\ + \x40\x03\x02\x02\x02\x17\x43\x03\x02\x02\x02\x19\x45\x03\x02\x02\x02\x1b\ + \x47\x03\x02\x02\x02\x1d\x49\x03\x02\x02\x02\x1f\x52\x03\x02\x02\x02\x21\ + \x57\x03\x02\x02\x02\x23\x5b\x03\x02\x02\x02\x25\x6b\x03\x02\x02\x02\x27\ + \x72\x03\x02\x02\x02\x29\x7b\x03\x02\x02\x02\x2b\x2c\x07\x2d\x02\x02\x2c\ + \x04\x03\x02\x02\x02\x2d\x2e\x07\x2e\x02\x02\x2e\x06\x03\x02\x02\x02\x2f\ + \x30\x07\x3f\x02\x02\x30\x08\x03\x02\x02\x02\x31\x32\x07\x7d\x02\x02\x32\ + \x0a\x03\x02\x02\x02\x33\x34\x07\x7f\x02\x02\x34\x0c\x03\x02\x02\x02\x35\ + \x36\x07\x6b\x02\x02\x36\x37\x07\x65\x02\x02\x37\x38\x07\x71\x02\x02\x38\ + \x39\x07\x70\x02\x02\x39\x0e\x03\x02\x02\x02\x3a\x3b\x07\x63\x02\x02\x3b\ + \x10\x03\x02\x02\x02\x3c\x3d\x07\x67\x02\x02\x3d\x12\x03\x02\x02\x02\x3e\ + \x3f\x07\x6e\x02\x02\x3f\x14\x03\x02\x02\x02\x40\x41\x07\x6e\x02\x02\x41\ + \x42\x07\x68\x02\x02\x42\x16\x03\x02\x02\x02\x43\x44\x07\x74\x02\x02\x44\ + \x18\x03\x02\x02\x02\x45\x46\x07\x6f\x02\x02\x46\x1a\x03\x02\x02\x02\x47\ + \x48\x07\x79\x02\x02\x48\x1c\x03\x02\x02\x02\x49\x4a\x07\x75\x02\x02\x4a\ + \x4b\x07\x77\x02\x02\x4b\x4c\x07\x64\x02\x02\x4c\x4d\x07\x76\x02\x02\x4d\ + \x4e\x07\x74\x02\x02\x4e\x4f\x07\x63\x02\x02\x4f\x50\x07\x65\x02\x02\x50\ + \x51\x07\x76\x02\x02\x51\x1e\x03\x02\x02\x02\x52\x53\x07\x68\x02\x02\x53\ + \x54\x07\x6b\x02\x02\x54\x55\x07\x6e\x02\x02\x55\x56\x07\x6e\x02\x02\x56\ + \x20\x03\x02\x02\x02\x57\x58\x07\x42\x02\x02\x58\x59\x05\x25\x13\x02\x59\ + \x22\x03\x02\x02\x02\x5a\x5c\x07\x2f\x02\x02\x5b\x5a\x03\x02\x02\x02\x5b\ + \x5c\x03\x02\x02\x02\x5c\x5e\x03\x02\x02\x02\x5d\x5f\x09\x02\x02\x02\x5e\ + \x5d\x03\x02\x02\x02\x5f\x60\x03\x02\x02\x02\x60\x5e\x03\x02\x02\x02\x60\ + \x61\x03\x02\x02\x02\x61\x69\x03\x02\x02\x02\x62\x66\x07\x30\x02\x02\x63\ + \x65\x09\x02\x02\x02\x64\x63\x03\x02\x02\x02\x65\x68\x03\x02\x02\x02\x66\ + \x64\x03\x02\x02\x02\x66\x67\x03\x02\x02\x02\x67\x6a\x03\x02\x02\x02\x68\ + \x66\x03\x02\x02\x02\x69\x62\x03\x02\x02\x02\x69\x6a\x03\x02\x02\x02\x6a\ + \x24\x03\x02\x02\x02\x6b\x6f\x09\x03\x02\x02\x6c\x6e\x09\x04\x02\x02\x6d\ + \x6c\x03\x02\x02\x02\x6e\x71\x03\x02\x02\x02\x6f\x6d\x03\x02\x02\x02\x6f\ + \x70\x03\x02\x02\x02\x70\x26\x03\x02\x02\x02\x71\x6f\x03\x02\x02\x02\x72\ + \x76\x07\x25\x02\x02\x73\x75\x0a\x05\x02\x02\x74\x73\x03\x02\x02\x02\x75\ + \x78\x03\x02\x02\x02\x76\x74\x03\x02\x02\x02\x76\x77\x03\x02\x02\x02\x77\ + \x79\x03\x02\x02\x02\x78\x76\x03\x02\x02\x02\x79\x7a\x08\x14\x02\x02\x7a\ + \x28\x03\x02\x02\x02\x7b\x7c\x09\x06\x02\x02\x7c\x7d\x03\x02\x02\x02\x7d\ + \x7e\x08\x15\x02\x02\x7e\x2a\x03\x02\x02\x02\x09\x02\x5b\x60\x66\x69\x6f\ + \x76\x03\x08\x02\x02"; diff --git a/rust/src/parser/iconscriptlistener.rs b/rust/src/parser/iconscriptlistener.rs index 0d15a00..30c7ca6 100644 --- a/rust/src/parser/iconscriptlistener.rs +++ b/rust/src/parser/iconscriptlistener.rs @@ -164,6 +164,16 @@ fn enter_setRemove(&mut self, _ctx: &SetRemoveContext<'input>) { } * @param ctx the parse tree */ fn exit_setRemove(&mut self, _ctx: &SetRemoveContext<'input>) { } +/** + * Enter a parse tree produced by {@link IconScriptParser#setFill}. + * @param ctx the parse tree + */ +fn enter_setFill(&mut self, _ctx: &SetFillContext<'input>) { } +/** + * Exit a parse tree produced by {@link IconScriptParser#setFill}. + * @param ctx the parse tree + */ +fn exit_setFill(&mut self, _ctx: &SetFillContext<'input>) { } } diff --git a/rust/src/parser/iconscriptparser.rs b/rust/src/parser/iconscriptparser.rs index 1959131..cccfe3e 100644 --- a/rust/src/parser/iconscriptparser.rs +++ b/rust/src/parser/iconscriptparser.rs @@ -53,11 +53,12 @@ use std::any::{Any,TypeId}; pub const T__11:isize=12; pub const T__12:isize=13; pub const T__13:isize=14; - pub const VARIABLE:isize=15; - pub const FLOAT:isize=16; - pub const IDENTIFIER:isize=17; - pub const COMMENT:isize=18; - pub const WS:isize=19; + pub const T__14:isize=15; + pub const VARIABLE:isize=16; + pub const FLOAT:isize=17; + pub const IDENTIFIER:isize=18; + pub const COMMENT:isize=19; + pub const WS:isize=20; pub const RULE_script:usize = 0; pub const RULE_position:usize = 1; pub const RULE_expression:usize = 2; @@ -73,22 +74,23 @@ use std::any::{Any,TypeId}; pub const RULE_name:usize = 12; pub const RULE_setPosition:usize = 13; pub const RULE_setWidth:usize = 14; - pub const RULE_setRemove:usize = 15; - pub const ruleNames: [&'static str; 16] = [ + pub const RULE_setRemove:usize = 15; + pub const RULE_setFill:usize = 16; + pub const ruleNames: [&'static str; 17] = [ "script", "position", "expression", "assignment", "scope", "commands", "icon", "command", "arc", "circle", "line", "rectangle", "name", "setPosition", - "setWidth", "setRemove" + "setWidth", "setRemove", "setFill" ]; - pub const _LITERAL_NAMES: [Option<&'static str>;15] = [ + pub const _LITERAL_NAMES: [Option<&'static str>;16] = [ None, Some("'+'"), Some("','"), Some("'='"), Some("'{'"), Some("'}'"), Some("'icon'"), Some("'a'"), Some("'e'"), Some("'l'"), Some("'lf'"), Some("'r'"), - Some("'m'"), Some("'w'"), Some("'subtract'") + Some("'m'"), Some("'w'"), Some("'subtract'"), Some("'fill'") ]; - pub const _SYMBOLIC_NAMES: [Option<&'static str>;20] = [ + pub const _SYMBOLIC_NAMES: [Option<&'static str>;21] = [ None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, Some("VARIABLE"), Some("FLOAT"), Some("IDENTIFIER"), + None, None, None, None, Some("VARIABLE"), Some("FLOAT"), Some("IDENTIFIER"), Some("COMMENT"), Some("WS") ]; lazy_static!{ @@ -327,19 +329,19 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(35); + recog.base.set_state(37); recog.err_handler.sync(&mut recog.base)?; _la = recog.base.input.la(1); while _la==T__5 || _la==IDENTIFIER { { { /*InvokeRule expression*/ - recog.base.set_state(32); + recog.base.set_state(34); recog.expression()?; } } - recog.base.set_state(37); + recog.base.set_state(39); recog.err_handler.sync(&mut recog.base)?; _la = recog.base.input.la(1); } @@ -446,12 +448,12 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(39); + recog.base.set_state(41); recog.err_handler.sync(&mut recog.base)?; _la = recog.base.input.la(1); if _la==T__0 { { - recog.base.set_state(38); + recog.base.set_state(40); let tmp = recog.base.match_token(T__0,&mut recog.err_handler)?; cast_mut::<_,PositionContext >(&mut _localctx).relative = Some(tmp.clone()); @@ -459,15 +461,15 @@ where } } - recog.base.set_state(41); + recog.base.set_state(43); let tmp = recog.base.match_token(FLOAT,&mut recog.err_handler)?; cast_mut::<_,PositionContext >(&mut _localctx).x = Some(tmp.clone()); - recog.base.set_state(42); + recog.base.set_state(44); recog.base.match_token(T__1,&mut recog.err_handler)?; - recog.base.set_state(43); + recog.base.set_state(45); let tmp = recog.base.match_token(FLOAT,&mut recog.err_handler)?; cast_mut::<_,PositionContext >(&mut _localctx).y = Some(tmp.clone()); @@ -564,7 +566,7 @@ where let mut _localctx: Rc = _localctx; let result: Result<(), ANTLRError> = (|| { - recog.base.set_state(47); + recog.base.set_state(49); recog.err_handler.sync(&mut recog.base)?; match recog.base.input.la(1) { IDENTIFIER @@ -573,7 +575,7 @@ where recog.base.enter_outer_alt(None, 1); { /*InvokeRule assignment*/ - recog.base.set_state(45); + recog.base.set_state(47); recog.assignment()?; } @@ -585,7 +587,7 @@ where recog.base.enter_outer_alt(None, 2); { /*InvokeRule icon*/ - recog.base.set_state(46); + recog.base.set_state(48); recog.icon()?; } @@ -693,16 +695,16 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(49); + recog.base.set_state(51); let tmp = recog.base.match_token(IDENTIFIER,&mut recog.err_handler)?; cast_mut::<_,AssignmentContext >(&mut _localctx).left = Some(tmp.clone()); - recog.base.set_state(50); + recog.base.set_state(52); recog.base.match_token(T__2,&mut recog.err_handler)?; /*InvokeRule commands*/ - recog.base.set_state(51); + recog.base.set_state(53); let tmp = recog.commands()?; cast_mut::<_,AssignmentContext >(&mut _localctx).right = Some(tmp.clone()); @@ -799,14 +801,14 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(53); + recog.base.set_state(55); recog.base.match_token(T__3,&mut recog.err_handler)?; /*InvokeRule commands*/ - recog.base.set_state(54); + recog.base.set_state(56); recog.commands()?; - recog.base.set_state(55); + recog.base.set_state(57); recog.base.match_token(T__4,&mut recog.err_handler)?; } @@ -911,22 +913,22 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(59); + recog.base.set_state(61); recog.err_handler.sync(&mut recog.base)?; _alt = 1; loop { match _alt { x if x == 1=> { - recog.base.set_state(59); + recog.base.set_state(61); recog.err_handler.sync(&mut recog.base)?; match recog.base.input.la(1) { - T__6 | T__7 | T__8 | T__9 | T__10 | T__11 | T__12 | T__13 | VARIABLE | - IDENTIFIER + T__6 | T__7 | T__8 | T__9 | T__10 | T__11 | T__12 | T__13 | T__14 | + VARIABLE | IDENTIFIER => { { /*InvokeRule command*/ - recog.base.set_state(57); + recog.base.set_state(59); recog.command()?; } @@ -936,7 +938,7 @@ where => { { /*InvokeRule scope*/ - recog.base.set_state(58); + recog.base.set_state(60); recog.scope()?; } @@ -948,7 +950,7 @@ where _ => Err(ANTLRError::NoAltError(NoViableAltError::new(&mut recog.base)))? } - recog.base.set_state(61); + recog.base.set_state(63); recog.err_handler.sync(&mut recog.base)?; _alt = recog.interpreter.adaptive_predict(4,&mut recog.base)?; if _alt==2 || _alt==INVALID_ALT { break } @@ -1048,31 +1050,31 @@ where let mut _localctx: Rc = _localctx; let result: Result<(), ANTLRError> = (|| { - recog.base.set_state(75); + recog.base.set_state(77); recog.err_handler.sync(&mut recog.base)?; match recog.interpreter.adaptive_predict(5,&mut recog.base)? { 1 =>{ //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(63); + recog.base.set_state(65); recog.base.match_token(T__5,&mut recog.err_handler)?; /*InvokeRule name*/ - recog.base.set_state(64); + recog.base.set_state(66); recog.name()?; - recog.base.set_state(65); + recog.base.set_state(67); recog.base.match_token(T__2,&mut recog.err_handler)?; - recog.base.set_state(66); + recog.base.set_state(68); recog.base.match_token(T__3,&mut recog.err_handler)?; /*InvokeRule commands*/ - recog.base.set_state(67); + recog.base.set_state(69); recog.commands()?; - recog.base.set_state(68); + recog.base.set_state(70); recog.base.match_token(T__4,&mut recog.err_handler)?; } @@ -1082,18 +1084,18 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 2); recog.base.enter_outer_alt(None, 2); { - recog.base.set_state(70); + recog.base.set_state(72); recog.base.match_token(T__5,&mut recog.err_handler)?; /*InvokeRule name*/ - recog.base.set_state(71); + recog.base.set_state(73); recog.name()?; - recog.base.set_state(72); + recog.base.set_state(74); recog.base.match_token(T__2,&mut recog.err_handler)?; /*InvokeRule command*/ - recog.base.set_state(73); + recog.base.set_state(75); recog.command()?; } @@ -1196,6 +1198,9 @@ fn setRemove(&self) -> Option>> where Self:Sized{ fn setWidth(&self) -> Option>> where Self:Sized{ self.child_of_type(0) } +fn setFill(&self) -> Option>> where Self:Sized{ + self.child_of_type(0) +} } @@ -1215,7 +1220,7 @@ where let mut _localctx: Rc = _localctx; let result: Result<(), ANTLRError> = (|| { - recog.base.set_state(86); + recog.base.set_state(89); recog.err_handler.sync(&mut recog.base)?; match recog.base.input.la(1) { IDENTIFIER @@ -1224,7 +1229,7 @@ where recog.base.enter_outer_alt(None, 1); { /*InvokeRule name*/ - recog.base.set_state(77); + recog.base.set_state(79); recog.name()?; } @@ -1235,7 +1240,7 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 2); recog.base.enter_outer_alt(None, 2); { - recog.base.set_state(78); + recog.base.set_state(80); recog.base.match_token(VARIABLE,&mut recog.err_handler)?; } @@ -1247,7 +1252,7 @@ where recog.base.enter_outer_alt(None, 3); { /*InvokeRule arc*/ - recog.base.set_state(79); + recog.base.set_state(81); recog.arc()?; } @@ -1259,7 +1264,7 @@ where recog.base.enter_outer_alt(None, 4); { /*InvokeRule circle*/ - recog.base.set_state(80); + recog.base.set_state(82); recog.circle()?; } @@ -1271,7 +1276,7 @@ where recog.base.enter_outer_alt(None, 5); { /*InvokeRule line*/ - recog.base.set_state(81); + recog.base.set_state(83); recog.line()?; } @@ -1283,7 +1288,7 @@ where recog.base.enter_outer_alt(None, 6); { /*InvokeRule rectangle*/ - recog.base.set_state(82); + recog.base.set_state(84); recog.rectangle()?; } @@ -1295,7 +1300,7 @@ where recog.base.enter_outer_alt(None, 7); { /*InvokeRule setPosition*/ - recog.base.set_state(83); + recog.base.set_state(85); recog.setPosition()?; } @@ -1307,7 +1312,7 @@ where recog.base.enter_outer_alt(None, 8); { /*InvokeRule setRemove*/ - recog.base.set_state(84); + recog.base.set_state(86); recog.setRemove()?; } @@ -1319,12 +1324,24 @@ where recog.base.enter_outer_alt(None, 9); { /*InvokeRule setWidth*/ - recog.base.set_state(85); + recog.base.set_state(87); recog.setWidth()?; } } + T__14 + => { + //recog.base.enter_outer_alt(_localctx.clone(), 10); + recog.base.enter_outer_alt(None, 10); + { + /*InvokeRule setFill*/ + recog.base.set_state(88); + recog.setFill()?; + + } + } + _ => Err(ANTLRError::NoAltError(NoViableAltError::new(&mut recog.base)))? } Ok(()) @@ -1427,20 +1444,20 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(88); + recog.base.set_state(91); recog.base.match_token(T__6,&mut recog.err_handler)?; /*InvokeRule position*/ - recog.base.set_state(89); + recog.base.set_state(92); recog.position()?; - recog.base.set_state(90); + recog.base.set_state(93); recog.base.match_token(FLOAT,&mut recog.err_handler)?; - recog.base.set_state(91); + recog.base.set_state(94); recog.base.match_token(FLOAT,&mut recog.err_handler)?; - recog.base.set_state(92); + recog.base.set_state(95); recog.base.match_token(FLOAT,&mut recog.err_handler)?; } @@ -1540,14 +1557,14 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(94); + recog.base.set_state(97); recog.base.match_token(T__7,&mut recog.err_handler)?; /*InvokeRule position*/ - recog.base.set_state(95); + recog.base.set_state(98); recog.position()?; - recog.base.set_state(96); + recog.base.set_state(99); recog.base.match_token(FLOAT,&mut recog.err_handler)?; } @@ -1646,7 +1663,7 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(98); + recog.base.set_state(101); _la = recog.base.input.la(1); if { !(_la==T__8 || _la==T__9) } { recog.err_handler.recover_inline(&mut recog.base)?; @@ -1657,19 +1674,19 @@ where recog.err_handler.report_match(&mut recog.base); recog.base.consume(&mut recog.err_handler); } - recog.base.set_state(100); + recog.base.set_state(103); recog.err_handler.sync(&mut recog.base)?; _la = recog.base.input.la(1); loop { { { /*InvokeRule position*/ - recog.base.set_state(99); + recog.base.set_state(102); recog.position()?; } } - recog.base.set_state(102); + recog.base.set_state(105); recog.err_handler.sync(&mut recog.base)?; _la = recog.base.input.la(1); if !(_la==T__0 || _la==FLOAT) {break} @@ -1769,15 +1786,15 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(104); + recog.base.set_state(107); recog.base.match_token(T__10,&mut recog.err_handler)?; /*InvokeRule position*/ - recog.base.set_state(105); + recog.base.set_state(108); recog.position()?; /*InvokeRule position*/ - recog.base.set_state(106); + recog.base.set_state(109); recog.position()?; } @@ -1874,7 +1891,7 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(108); + recog.base.set_state(111); recog.base.match_token(IDENTIFIER,&mut recog.err_handler)?; } @@ -1969,11 +1986,11 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(110); + recog.base.set_state(113); recog.base.match_token(T__11,&mut recog.err_handler)?; /*InvokeRule position*/ - recog.base.set_state(111); + recog.base.set_state(114); recog.position()?; } @@ -2070,10 +2087,10 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(113); + recog.base.set_state(116); recog.base.match_token(T__12,&mut recog.err_handler)?; - recog.base.set_state(114); + recog.base.set_state(117); recog.base.match_token(FLOAT,&mut recog.err_handler)?; } @@ -2165,7 +2182,7 @@ where //recog.base.enter_outer_alt(_localctx.clone(), 1); recog.base.enter_outer_alt(None, 1); { - recog.base.set_state(116); + recog.base.set_state(119); recog.base.match_token(T__13,&mut recog.err_handler)?; } @@ -2185,6 +2202,98 @@ where Ok(_localctx) } } +//------------------- setFill ---------------- +pub type SetFillContextAll<'input> = SetFillContext<'input>; + + +pub type SetFillContext<'input> = BaseParserRuleContext<'input,SetFillContextExt<'input>>; + +#[derive(Clone)] +pub struct SetFillContextExt<'input>{ +ph:PhantomData<&'input str> +} + +impl<'input> IconScriptParserContext<'input> for SetFillContext<'input>{} + +impl<'input,'a> Listenable + 'a> for SetFillContext<'input>{ + fn enter(&self,listener: &mut (dyn IconScriptListener<'input> + 'a)) { + listener.enter_every_rule(self); + listener.enter_setFill(self); + } + fn exit(&self,listener: &mut (dyn IconScriptListener<'input> + 'a)) { + listener.exit_setFill(self); + listener.exit_every_rule(self); + } +} + +impl<'input,'a> Visitable + 'a> for SetFillContext<'input>{ + fn accept(&self,visitor: &mut (dyn IconScriptVisitor<'input> + 'a)) { + visitor.visit_setFill(self); + } +} + +impl<'input> CustomRuleContext<'input> for SetFillContextExt<'input>{ + type TF = LocalTokenFactory<'input>; + type Ctx = IconScriptParserContextType; + fn get_rule_index(&self) -> usize { RULE_setFill } + //fn type_rule_index() -> usize where Self: Sized { RULE_setFill } +} +antlr_rust::tid!{SetFillContextExt<'a>} + +impl<'input> SetFillContextExt<'input>{ + fn new(parent: Option + 'input > >, invoking_state: isize) -> Rc> { + Rc::new( + BaseParserRuleContext::new_parser_ctx(parent, invoking_state,SetFillContextExt{ + ph:PhantomData + }), + ) + } +} + +pub trait SetFillContextAttrs<'input>: IconScriptParserContext<'input> + BorrowMut>{ + + +} + +impl<'input> SetFillContextAttrs<'input> for SetFillContext<'input>{} + +impl<'input, I, H> IconScriptParser<'input, I, H> +where + I: TokenStream<'input, TF = LocalTokenFactory<'input> > + TidAble<'input>, + H: ErrorStrategy<'input,BaseParserType<'input,I>> +{ + pub fn setFill(&mut self,) + -> Result>,ANTLRError> { + let mut recog = self; + let _parentctx = recog.ctx.take(); + let mut _localctx = SetFillContextExt::new(_parentctx.clone(), recog.base.get_state()); + recog.base.enter_rule(_localctx.clone(), 32, RULE_setFill); + let mut _localctx: Rc = _localctx; + let result: Result<(), ANTLRError> = (|| { + + //recog.base.enter_outer_alt(_localctx.clone(), 1); + recog.base.enter_outer_alt(None, 1); + { + recog.base.set_state(121); + recog.base.match_token(T__14,&mut recog.err_handler)?; + + } + Ok(()) + })(); + match result { + Ok(_)=>{}, + Err(e @ ANTLRError::FallThrough(_)) => return Err(e), + Err(ref re) => { + //_localctx.exception = re; + recog.err_handler.report_error(&mut recog.base, re); + recog.err_handler.recover(&mut recog.base, re)?; + } + } + recog.base.exit_rule(); + + Ok(_localctx) + } +} lazy_static! { static ref _ATN: Arc = @@ -2207,59 +2316,62 @@ lazy_static! { const _serializedATN:&'static str = "\x03\u{608b}\u{a72a}\u{8133}\u{b9ed}\u{417c}\u{3be7}\u{7786}\u{5964}\x03\ - \x15\x79\x04\x02\x09\x02\x04\x03\x09\x03\x04\x04\x09\x04\x04\x05\x09\x05\ + \x16\x7e\x04\x02\x09\x02\x04\x03\x09\x03\x04\x04\x09\x04\x04\x05\x09\x05\ \x04\x06\x09\x06\x04\x07\x09\x07\x04\x08\x09\x08\x04\x09\x09\x09\x04\x0a\ \x09\x0a\x04\x0b\x09\x0b\x04\x0c\x09\x0c\x04\x0d\x09\x0d\x04\x0e\x09\x0e\ - \x04\x0f\x09\x0f\x04\x10\x09\x10\x04\x11\x09\x11\x03\x02\x07\x02\x24\x0a\ - \x02\x0c\x02\x0e\x02\x27\x0b\x02\x03\x03\x05\x03\x2a\x0a\x03\x03\x03\x03\ - \x03\x03\x03\x03\x03\x03\x04\x03\x04\x05\x04\x32\x0a\x04\x03\x05\x03\x05\ - \x03\x05\x03\x05\x03\x06\x03\x06\x03\x06\x03\x06\x03\x07\x03\x07\x06\x07\ - \x3e\x0a\x07\x0d\x07\x0e\x07\x3f\x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\ - \x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\x05\x08\x4e\x0a\ - \x08\x03\x09\x03\x09\x03\x09\x03\x09\x03\x09\x03\x09\x03\x09\x03\x09\x03\ - \x09\x05\x09\x59\x0a\x09\x03\x0a\x03\x0a\x03\x0a\x03\x0a\x03\x0a\x03\x0a\ - \x03\x0b\x03\x0b\x03\x0b\x03\x0b\x03\x0c\x03\x0c\x06\x0c\x67\x0a\x0c\x0d\ - \x0c\x0e\x0c\x68\x03\x0d\x03\x0d\x03\x0d\x03\x0d\x03\x0e\x03\x0e\x03\x0f\ - \x03\x0f\x03\x0f\x03\x10\x03\x10\x03\x10\x03\x11\x03\x11\x03\x11\x02\x02\ - \x12\x02\x04\x06\x08\x0a\x0c\x0e\x10\x12\x14\x16\x18\x1a\x1c\x1e\x20\x02\ - \x03\x03\x02\x0b\x0c\x02\x77\x02\x25\x03\x02\x02\x02\x04\x29\x03\x02\x02\ - \x02\x06\x31\x03\x02\x02\x02\x08\x33\x03\x02\x02\x02\x0a\x37\x03\x02\x02\ - \x02\x0c\x3d\x03\x02\x02\x02\x0e\x4d\x03\x02\x02\x02\x10\x58\x03\x02\x02\ - \x02\x12\x5a\x03\x02\x02\x02\x14\x60\x03\x02\x02\x02\x16\x64\x03\x02\x02\ - \x02\x18\x6a\x03\x02\x02\x02\x1a\x6e\x03\x02\x02\x02\x1c\x70\x03\x02\x02\ - \x02\x1e\x73\x03\x02\x02\x02\x20\x76\x03\x02\x02\x02\x22\x24\x05\x06\x04\ - \x02\x23\x22\x03\x02\x02\x02\x24\x27\x03\x02\x02\x02\x25\x23\x03\x02\x02\ - \x02\x25\x26\x03\x02\x02\x02\x26\x03\x03\x02\x02\x02\x27\x25\x03\x02\x02\ - \x02\x28\x2a\x07\x03\x02\x02\x29\x28\x03\x02\x02\x02\x29\x2a\x03\x02\x02\ - \x02\x2a\x2b\x03\x02\x02\x02\x2b\x2c\x07\x12\x02\x02\x2c\x2d\x07\x04\x02\ - \x02\x2d\x2e\x07\x12\x02\x02\x2e\x05\x03\x02\x02\x02\x2f\x32\x05\x08\x05\ - \x02\x30\x32\x05\x0e\x08\x02\x31\x2f\x03\x02\x02\x02\x31\x30\x03\x02\x02\ - \x02\x32\x07\x03\x02\x02\x02\x33\x34\x07\x13\x02\x02\x34\x35\x07\x05\x02\ - \x02\x35\x36\x05\x0c\x07\x02\x36\x09\x03\x02\x02\x02\x37\x38\x07\x06\x02\ - \x02\x38\x39\x05\x0c\x07\x02\x39\x3a\x07\x07\x02\x02\x3a\x0b\x03\x02\x02\ - \x02\x3b\x3e\x05\x10\x09\x02\x3c\x3e\x05\x0a\x06\x02\x3d\x3b\x03\x02\x02\ - \x02\x3d\x3c\x03\x02\x02\x02\x3e\x3f\x03\x02\x02\x02\x3f\x3d\x03\x02\x02\ - \x02\x3f\x40\x03\x02\x02\x02\x40\x0d\x03\x02\x02\x02\x41\x42\x07\x08\x02\ - \x02\x42\x43\x05\x1a\x0e\x02\x43\x44\x07\x05\x02\x02\x44\x45\x07\x06\x02\ - \x02\x45\x46\x05\x0c\x07\x02\x46\x47\x07\x07\x02\x02\x47\x4e\x03\x02\x02\ - \x02\x48\x49\x07\x08\x02\x02\x49\x4a\x05\x1a\x0e\x02\x4a\x4b\x07\x05\x02\ - \x02\x4b\x4c\x05\x10\x09\x02\x4c\x4e\x03\x02\x02\x02\x4d\x41\x03\x02\x02\ - \x02\x4d\x48\x03\x02\x02\x02\x4e\x0f\x03\x02\x02\x02\x4f\x59\x05\x1a\x0e\ - \x02\x50\x59\x07\x11\x02\x02\x51\x59\x05\x12\x0a\x02\x52\x59\x05\x14\x0b\ - \x02\x53\x59\x05\x16\x0c\x02\x54\x59\x05\x18\x0d\x02\x55\x59\x05\x1c\x0f\ - \x02\x56\x59\x05\x20\x11\x02\x57\x59\x05\x1e\x10\x02\x58\x4f\x03\x02\x02\ - \x02\x58\x50\x03\x02\x02\x02\x58\x51\x03\x02\x02\x02\x58\x52\x03\x02\x02\ - \x02\x58\x53\x03\x02\x02\x02\x58\x54\x03\x02\x02\x02\x58\x55\x03\x02\x02\ - \x02\x58\x56\x03\x02\x02\x02\x58\x57\x03\x02\x02\x02\x59\x11\x03\x02\x02\ - \x02\x5a\x5b\x07\x09\x02\x02\x5b\x5c\x05\x04\x03\x02\x5c\x5d\x07\x12\x02\ - \x02\x5d\x5e\x07\x12\x02\x02\x5e\x5f\x07\x12\x02\x02\x5f\x13\x03\x02\x02\ - \x02\x60\x61\x07\x0a\x02\x02\x61\x62\x05\x04\x03\x02\x62\x63\x07\x12\x02\ - \x02\x63\x15\x03\x02\x02\x02\x64\x66\x09\x02\x02\x02\x65\x67\x05\x04\x03\ - \x02\x66\x65\x03\x02\x02\x02\x67\x68\x03\x02\x02\x02\x68\x66\x03\x02\x02\ - \x02\x68\x69\x03\x02\x02\x02\x69\x17\x03\x02\x02\x02\x6a\x6b\x07\x0d\x02\ - \x02\x6b\x6c\x05\x04\x03\x02\x6c\x6d\x05\x04\x03\x02\x6d\x19\x03\x02\x02\ - \x02\x6e\x6f\x07\x13\x02\x02\x6f\x1b\x03\x02\x02\x02\x70\x71\x07\x0e\x02\ - \x02\x71\x72\x05\x04\x03\x02\x72\x1d\x03\x02\x02\x02\x73\x74\x07\x0f\x02\ - \x02\x74\x75\x07\x12\x02\x02\x75\x1f\x03\x02\x02\x02\x76\x77\x07\x10\x02\ - \x02\x77\x21\x03\x02\x02\x02\x0a\x25\x29\x31\x3d\x3f\x4d\x58\x68"; + \x04\x0f\x09\x0f\x04\x10\x09\x10\x04\x11\x09\x11\x04\x12\x09\x12\x03\x02\ + \x07\x02\x26\x0a\x02\x0c\x02\x0e\x02\x29\x0b\x02\x03\x03\x05\x03\x2c\x0a\ + \x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x04\x05\x04\x34\x0a\x04\ + \x03\x05\x03\x05\x03\x05\x03\x05\x03\x06\x03\x06\x03\x06\x03\x06\x03\x07\ + \x03\x07\x06\x07\x40\x0a\x07\x0d\x07\x0e\x07\x41\x03\x08\x03\x08\x03\x08\ + \x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\x03\x08\ + \x05\x08\x50\x0a\x08\x03\x09\x03\x09\x03\x09\x03\x09\x03\x09\x03\x09\x03\ + \x09\x03\x09\x03\x09\x03\x09\x05\x09\x5c\x0a\x09\x03\x0a\x03\x0a\x03\x0a\ + \x03\x0a\x03\x0a\x03\x0a\x03\x0b\x03\x0b\x03\x0b\x03\x0b\x03\x0c\x03\x0c\ + \x06\x0c\x6a\x0a\x0c\x0d\x0c\x0e\x0c\x6b\x03\x0d\x03\x0d\x03\x0d\x03\x0d\ + \x03\x0e\x03\x0e\x03\x0f\x03\x0f\x03\x0f\x03\x10\x03\x10\x03\x10\x03\x11\ + \x03\x11\x03\x12\x03\x12\x03\x12\x02\x02\x13\x02\x04\x06\x08\x0a\x0c\x0e\ + \x10\x12\x14\x16\x18\x1a\x1c\x1e\x20\x22\x02\x03\x03\x02\x0b\x0c\x02\x7c\ + \x02\x27\x03\x02\x02\x02\x04\x2b\x03\x02\x02\x02\x06\x33\x03\x02\x02\x02\ + \x08\x35\x03\x02\x02\x02\x0a\x39\x03\x02\x02\x02\x0c\x3f\x03\x02\x02\x02\ + \x0e\x4f\x03\x02\x02\x02\x10\x5b\x03\x02\x02\x02\x12\x5d\x03\x02\x02\x02\ + \x14\x63\x03\x02\x02\x02\x16\x67\x03\x02\x02\x02\x18\x6d\x03\x02\x02\x02\ + \x1a\x71\x03\x02\x02\x02\x1c\x73\x03\x02\x02\x02\x1e\x76\x03\x02\x02\x02\ + \x20\x79\x03\x02\x02\x02\x22\x7b\x03\x02\x02\x02\x24\x26\x05\x06\x04\x02\ + \x25\x24\x03\x02\x02\x02\x26\x29\x03\x02\x02\x02\x27\x25\x03\x02\x02\x02\ + \x27\x28\x03\x02\x02\x02\x28\x03\x03\x02\x02\x02\x29\x27\x03\x02\x02\x02\ + \x2a\x2c\x07\x03\x02\x02\x2b\x2a\x03\x02\x02\x02\x2b\x2c\x03\x02\x02\x02\ + \x2c\x2d\x03\x02\x02\x02\x2d\x2e\x07\x13\x02\x02\x2e\x2f\x07\x04\x02\x02\ + \x2f\x30\x07\x13\x02\x02\x30\x05\x03\x02\x02\x02\x31\x34\x05\x08\x05\x02\ + \x32\x34\x05\x0e\x08\x02\x33\x31\x03\x02\x02\x02\x33\x32\x03\x02\x02\x02\ + \x34\x07\x03\x02\x02\x02\x35\x36\x07\x14\x02\x02\x36\x37\x07\x05\x02\x02\ + \x37\x38\x05\x0c\x07\x02\x38\x09\x03\x02\x02\x02\x39\x3a\x07\x06\x02\x02\ + \x3a\x3b\x05\x0c\x07\x02\x3b\x3c\x07\x07\x02\x02\x3c\x0b\x03\x02\x02\x02\ + \x3d\x40\x05\x10\x09\x02\x3e\x40\x05\x0a\x06\x02\x3f\x3d\x03\x02\x02\x02\ + \x3f\x3e\x03\x02\x02\x02\x40\x41\x03\x02\x02\x02\x41\x3f\x03\x02\x02\x02\ + \x41\x42\x03\x02\x02\x02\x42\x0d\x03\x02\x02\x02\x43\x44\x07\x08\x02\x02\ + \x44\x45\x05\x1a\x0e\x02\x45\x46\x07\x05\x02\x02\x46\x47\x07\x06\x02\x02\ + \x47\x48\x05\x0c\x07\x02\x48\x49\x07\x07\x02\x02\x49\x50\x03\x02\x02\x02\ + \x4a\x4b\x07\x08\x02\x02\x4b\x4c\x05\x1a\x0e\x02\x4c\x4d\x07\x05\x02\x02\ + \x4d\x4e\x05\x10\x09\x02\x4e\x50\x03\x02\x02\x02\x4f\x43\x03\x02\x02\x02\ + \x4f\x4a\x03\x02\x02\x02\x50\x0f\x03\x02\x02\x02\x51\x5c\x05\x1a\x0e\x02\ + \x52\x5c\x07\x12\x02\x02\x53\x5c\x05\x12\x0a\x02\x54\x5c\x05\x14\x0b\x02\ + \x55\x5c\x05\x16\x0c\x02\x56\x5c\x05\x18\x0d\x02\x57\x5c\x05\x1c\x0f\x02\ + \x58\x5c\x05\x20\x11\x02\x59\x5c\x05\x1e\x10\x02\x5a\x5c\x05\x22\x12\x02\ + \x5b\x51\x03\x02\x02\x02\x5b\x52\x03\x02\x02\x02\x5b\x53\x03\x02\x02\x02\ + \x5b\x54\x03\x02\x02\x02\x5b\x55\x03\x02\x02\x02\x5b\x56\x03\x02\x02\x02\ + \x5b\x57\x03\x02\x02\x02\x5b\x58\x03\x02\x02\x02\x5b\x59\x03\x02\x02\x02\ + \x5b\x5a\x03\x02\x02\x02\x5c\x11\x03\x02\x02\x02\x5d\x5e\x07\x09\x02\x02\ + \x5e\x5f\x05\x04\x03\x02\x5f\x60\x07\x13\x02\x02\x60\x61\x07\x13\x02\x02\ + \x61\x62\x07\x13\x02\x02\x62\x13\x03\x02\x02\x02\x63\x64\x07\x0a\x02\x02\ + \x64\x65\x05\x04\x03\x02\x65\x66\x07\x13\x02\x02\x66\x15\x03\x02\x02\x02\ + \x67\x69\x09\x02\x02\x02\x68\x6a\x05\x04\x03\x02\x69\x68\x03\x02\x02\x02\ + \x6a\x6b\x03\x02\x02\x02\x6b\x69\x03\x02\x02\x02\x6b\x6c\x03\x02\x02\x02\ + \x6c\x17\x03\x02\x02\x02\x6d\x6e\x07\x0d\x02\x02\x6e\x6f\x05\x04\x03\x02\ + \x6f\x70\x05\x04\x03\x02\x70\x19\x03\x02\x02\x02\x71\x72\x07\x14\x02\x02\ + \x72\x1b\x03\x02\x02\x02\x73\x74\x07\x0e\x02\x02\x74\x75\x05\x04\x03\x02\ + \x75\x1d\x03\x02\x02\x02\x76\x77\x07\x0f\x02\x02\x77\x78\x07\x13\x02\x02\ + \x78\x1f\x03\x02\x02\x02\x79\x7a\x07\x10\x02\x02\x7a\x21\x03\x02\x02\x02\ + \x7b\x7c\x07\x11\x02\x02\x7c\x23\x03\x02\x02\x02\x0a\x27\x2b\x33\x3f\x41\ + \x4f\x5b\x6b"; diff --git a/rust/src/parser/iconscriptvisitor.rs b/rust/src/parser/iconscriptvisitor.rs index a82dd0d..3e61926 100644 --- a/rust/src/parser/iconscriptvisitor.rs +++ b/rust/src/parser/iconscriptvisitor.rs @@ -104,6 +104,12 @@ pub trait IconScriptVisitor<'input>: ParseTreeVisitor<'input,IconScriptParserCon */ fn visit_setRemove(&mut self, ctx: &SetRemoveContext<'input>) { self.visit_children(ctx) } + /** + * Visit a parse tree produced by {@link IconScriptParser#setFill}. + * @param ctx the parse tree + */ + fn visit_setFill(&mut self, ctx: &SetFillContext<'input>) { self.visit_children(ctx) } + } pub trait IconScriptVisitorCompat<'input>:ParseTreeVisitorCompat<'input, Node= IconScriptParserContextType>{ @@ -235,6 +241,14 @@ pub trait IconScriptVisitorCompat<'input>:ParseTreeVisitorCompat<'input, Node= I self.visit_children(ctx) } + /** + * Visit a parse tree produced by {@link IconScriptParser#setFill}. + * @param ctx the parse tree + */ + fn visit_setFill(&mut self, ctx: &SetFillContext<'input>) -> Self::Return { + self.visit_children(ctx) + } + } impl<'input,T> IconScriptVisitor<'input> for T @@ -321,4 +335,9 @@ where *::temp_result(self) = result; } + fn visit_setFill(&mut self, ctx: &SetFillContext<'input>){ + let result = ::visit_setFill(self, ctx); + *::temp_result(self) = result; + } + } \ No newline at end of file diff --git a/rust/src/types.rs b/rust/src/types.rs index 88c2b9a..e0f0eb5 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -21,6 +21,7 @@ pub struct Scope { pub uniting: bool, pub width: f64, pub position: Point, + pub is_filled: bool, } impl Scope { @@ -29,6 +30,7 @@ impl Scope { uniting: true, width: 1.0, position: Point::new(0.0, 0.0), + is_filled: false, } } From 7d3c63410f775c9f48790d951e2281cb45937ed8 Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Thu, 22 Jan 2026 02:51:06 +0400 Subject: [PATCH 2/7] [#9] Make rectangles use fill status --- js/src/parser.ts | 69 +++++++++++++---------- rust/src/generator.rs | 27 +++++---- rust/src/listener.rs | 127 ++++++++++++++++++++++++++---------------- rust/src/types.rs | 21 +------ 4 files changed, 133 insertions(+), 111 deletions(-) diff --git a/js/src/parser.ts b/js/src/parser.ts index e48cb31..0a1faa2 100644 --- a/js/src/parser.ts +++ b/js/src/parser.ts @@ -128,15 +128,13 @@ function unionPathsWithModes(paths: string[], modes: boolean[]): string | null { * Create a thick line path. */ function createThickLinePath( - x1: number, - y1: number, - x2: number, - y2: number, + from: Point, + to: Point, thickness: number, ): string | null { // Create a thick line by offsetting the line perpendicular to its direction. - const dx = x2 - x1; - const dy = y2 - y1; + const dx = to.x - from.x; + const dy = to.y - from.y; const length = Math.sqrt(dx * dx + dy * dy); if (length === 0) return null; @@ -151,14 +149,14 @@ function createThickLinePath( // Create a rectangle path. const halfThickness = thickness / 2; - const x1a = x1 + px * halfThickness; - const y1a = y1 + py * halfThickness; - const x1b = x1 - px * halfThickness; - const y1b = y1 - py * halfThickness; - const x2a = x2 + px * halfThickness; - const y2a = y2 + py * halfThickness; - const x2b = x2 - px * halfThickness; - const y2b = y2 - py * halfThickness; + const x1a = from.x + px * halfThickness; + const y1a = from.y + py * halfThickness; + const x1b = from.x - px * halfThickness; + const y1b = from.y - py * halfThickness; + const x2a = to.x + px * halfThickness; + const y2a = to.y + py * halfThickness; + const x2b = to.x - px * halfThickness; + const y2b = to.y - py * halfThickness; return `M ${x1a} ${y1a} L ${x2a} ${y2a} L ${x2b} ${y2b} L ${x1b} ${y1b} Z`; } @@ -340,6 +338,14 @@ class IconScriptListener extends GeneratedIconScriptListener { } }; + line = (from: Point, to: Point): void => { + const linePath = createThickLinePath(from, to, this.getScope().width); + if (linePath) { + this.paths.push(linePath); + this.modes.push(this.getScope().uniting); + } + }; + exitLine = (ctx: LineContext): void => { const isFilled = ctx.getText().includes("lf") || this.getScope().isFilled; @@ -370,10 +376,8 @@ class IconScriptListener extends GeneratedIconScriptListener { const from = coordinates[i]; const to = coordinates[i + 1]; const linePath = createThickLinePath( - from.x, - from.y, - to.x, - to.y, + from, + to, this.getScope().width, ); if (linePath) { @@ -522,18 +526,25 @@ class IconScriptListener extends GeneratedIconScriptListener { } const halfWidth = this.getScope().width / 2; - // Add filled rectangle as a filled polyline. - const rectanglePath1 = - `M ${point1.x - halfWidth} ${point1.y} L ${p1.x + halfWidth} ${p1.y} ` + - `L ${point2.x + halfWidth} ${point2.y} L ${p2.x - halfWidth} ${p2.y} Z`; - const rectanglePath2 = - `M ${point1.x} ${point1.y - halfWidth} L ${p1.x} ${p1.y - halfWidth} ` + - `L ${point2.x} ${point2.y + halfWidth} L ${p2.x} ${p2.y + halfWidth} Z`; + if (this.getScope().isFilled) { + // Add filled rectangle as a filled polyline. + const rectanglePath1 = + `M ${point1.x - halfWidth} ${point1.y} L ${p1.x + halfWidth} ${p1.y} ` + + `L ${point2.x + halfWidth} ${point2.y} L ${p2.x - halfWidth} ${p2.y} Z`; + const rectanglePath2 = + `M ${point1.x} ${point1.y - halfWidth} L ${p1.x} ${p1.y - halfWidth} ` + + `L ${point2.x} ${point2.y + halfWidth} L ${p2.x} ${p2.y + halfWidth} Z`; - this.paths.push(rectanglePath1); - this.modes.push(this.getScope().uniting); - this.paths.push(rectanglePath2); - this.modes.push(this.getScope().uniting); + this.paths.push(rectanglePath1); + this.modes.push(this.getScope().uniting); + this.paths.push(rectanglePath2); + this.modes.push(this.getScope().uniting); + } else { + this.line(point1, p1); + this.line(p1, point2); + this.line(point2, p2); + this.line(p2, point1); + } }; exitSetPosition = (ctx: SetPositionContext): void => { diff --git a/rust/src/generator.rs b/rust/src/generator.rs index 3e50872..9dfe6f3 100644 --- a/rust/src/generator.rs +++ b/rust/src/generator.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use kurbo::Point; use std::fs; use std::path::Path; @@ -47,14 +48,12 @@ pub fn create_circle_path(cx: f64, cy: f64, r: f64) -> Option { /// Create a thick line path as a rectangle. pub fn create_thick_line_path( - x1: f64, - y1: f64, - x2: f64, - y2: f64, + from: Point, + to: Point, thickness: f64, ) -> Option { - let dx = x2 - x1; - let dy = y2 - y1; + let dx = to.x - from.x; + let dy = to.y - from.y; let length = (dx * dx + dy * dy).sqrt(); if length == 0.0 { @@ -71,14 +70,14 @@ pub fn create_thick_line_path( // Create a rectangle path. let half_thickness = thickness / 2.0; - let x1a = x1 + px * half_thickness; - let y1a = y1 + py * half_thickness; - let x1b = x1 - px * half_thickness; - let y1b = y1 - py * half_thickness; - let x2a = x2 + px * half_thickness; - let y2a = y2 + py * half_thickness; - let x2b = x2 - px * half_thickness; - let y2b = y2 - py * half_thickness; + let x1a = from.x + px * half_thickness; + let y1a = from.y + py * half_thickness; + let x1b = from.x - px * half_thickness; + let y1b = from.y - py * half_thickness; + let x2a = to.x + px * half_thickness; + let y2a = to.y + py * half_thickness; + let x2b = to.x - px * half_thickness; + let y2b = to.y - py * half_thickness; Some(format!( "M {} {} L {} {} L {} {} L {} {} Z", diff --git a/rust/src/listener.rs b/rust/src/listener.rs index c7f820d..2e92a94 100644 --- a/rust/src/listener.rs +++ b/rust/src/listener.rs @@ -10,7 +10,7 @@ use std::rc::Rc; use crate::generator::{create_circle_path, create_thick_line_path}; use crate::parser::iconscriptparser::*; use crate::parser::*; -use crate::types::{Icon, PathWithMode, Point, Scope}; +use crate::types::{Icon, PathWithMode, Scope}; const SCALE: f64 = 1.0; @@ -19,7 +19,6 @@ pub fn parse_iconscript( content: &str, sketch_mode: bool, ) -> Result)>> { - let input = InputStream::new(content); let lexer = IconScriptLexer::new(input); let token_stream = CommonTokenStream::new(lexer); @@ -66,7 +65,7 @@ impl<'input> IconScriptListenerImpl<'input> { self.icons } - fn parse_position(&mut self, ctx: &PositionContext) -> Point { + fn parse_position(&mut self, ctx: &PositionContext) -> kurbo::Point { let x_text = ctx.x.as_ref().unwrap().get_text(); let y_text = ctx.y.as_ref().unwrap().get_text(); @@ -77,17 +76,21 @@ impl<'input> IconScriptListenerImpl<'input> { let position = if is_relative { let current = self.get_scope().position; - current.add(&Point::new(x, y)) + kurbo::Point::new(current.x + x, current.y + y) } else { - Point::new(x + 0.5, y + 0.5) + kurbo::Point::new(x + 0.5, y + 0.5) }; self.get_scope_mut().position = position; position } - fn arc_point(center: Point, angle: f64, radius: f64) -> Point { - Point::new( + fn arc_point( + center: kurbo::Point, + angle: f64, + radius: f64, + ) -> kurbo::Point { + kurbo::Point::new( center.x + angle.cos() * radius, center.y - angle.sin() * radius, ) @@ -162,7 +165,8 @@ impl<'input> IconScriptListenerImpl<'input> { } } -impl<'input> antlr_rust::tree::ParseTreeListener<'input, IconScriptParserContextType> +impl<'input> + antlr_rust::tree::ParseTreeListener<'input, IconScriptParserContextType> for IconScriptListenerImpl<'input> { } @@ -190,7 +194,6 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { } fn enter_command(&mut self, ctx: &CommandContext<'input>) { - // Check if this is a variable reference. if let Some(var_token) = ctx.VARIABLE() { let var_name = &var_token.get_text()[1..]; // Remove '@' prefix @@ -234,8 +237,9 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { } fn exit_line(&mut self, ctx: &LineContext<'input>) { - let is_filled = ctx.get_text().contains("lf") || self.get_scope().is_filled; - let positions: Vec = ctx + let is_filled = + ctx.get_text().contains("lf") || self.get_scope().is_filled; + let positions: Vec = ctx .position_all() .iter() .map(|pos| self.parse_position(pos)) @@ -263,9 +267,7 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { for i in 0..positions.len() - 1 { let from = positions[i]; let to = positions[i + 1]; - if let Some(path) = - create_thick_line_path(from.x, from.y, to.x, to.y, width) - { + if let Some(path) = create_thick_line_path(from, to, width) { self.paths.push(PathWithMode { path, mode: uniting, @@ -300,8 +302,8 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { let width = scope.width; let uniting = scope.uniting; - let p1 = Point::new(point2.x, point1.y); - let p2 = Point::new(point1.x, point2.y); + let p1 = kurbo::Point::new(point2.x, point1.y); + let p2 = kurbo::Point::new(point1.x, point2.y); // Add circles at all four corners. let corners = [point1, p1, point2, p2]; @@ -318,38 +320,65 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { let half_width = width / 2.0; - // Add filled rectangles. - let rect_path1 = format!( - "M {} {} L {} {} L {} {} L {} {} Z", - point1.x - half_width, - point1.y, - p1.x + half_width, - p1.y, - point2.x + half_width, - point2.y, - p2.x - half_width, - p2.y - ); - let rect_path2 = format!( - "M {} {} L {} {} L {} {} L {} {} Z", - point1.x, - point1.y - half_width, - p1.x, - p1.y - half_width, - point2.x, - point2.y + half_width, - p2.x, - p2.y + half_width - ); - - self.paths.push(PathWithMode { - path: rect_path1, - mode: uniting, - }); - self.paths.push(PathWithMode { - path: rect_path2, - mode: uniting, - }); + if self.get_scope_mut().is_filled { + // Add filled rectangles. + let rect_path1 = format!( + "M {} {} L {} {} L {} {} L {} {} Z", + point1.x - half_width, + point1.y, + p1.x + half_width, + p1.y, + point2.x + half_width, + point2.y, + p2.x - half_width, + p2.y + ); + let rect_path2 = format!( + "M {} {} L {} {} L {} {} L {} {} Z", + point1.x, + point1.y - half_width, + p1.x, + p1.y - half_width, + point2.x, + point2.y + half_width, + p2.x, + p2.y + half_width + ); + + self.paths.push(PathWithMode { + path: rect_path1, + mode: uniting, + }); + self.paths.push(PathWithMode { + path: rect_path2, + mode: uniting, + }); + } else { + if let Some(path) = create_thick_line_path(point1, p1, width) { + self.paths.push(PathWithMode { + path, + mode: uniting, + }); + } + if let Some(path) = create_thick_line_path(p1, point2, width) { + self.paths.push(PathWithMode { + path, + mode: uniting, + }); + } + if let Some(path) = create_thick_line_path(point2, p2, width) { + self.paths.push(PathWithMode { + path, + mode: uniting, + }); + } + if let Some(path) = create_thick_line_path(p2, point1, width) { + self.paths.push(PathWithMode { + path, + mode: uniting, + }); + } + } } fn exit_arc(&mut self, ctx: &ArcContext<'input>) { @@ -368,7 +397,7 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { let start_angle: f64 = floats[1].get_text().parse().unwrap_or(0.0); let end_angle: f64 = floats[2].get_text().parse().unwrap_or(0.0); - let center = Point::new(pos.x + 0.5, pos.y + 0.5); + let center = kurbo::Point::new(pos.x + 0.5, pos.y + 0.5); let tau = 2.0 * PI; let mut delta = end_angle - start_angle; diff --git a/rust/src/types.rs b/rust/src/types.rs index e0f0eb5..3b62cd5 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1,26 +1,9 @@ -/// Represents a point in 2D space. -#[derive(Debug, Clone, Copy)] -pub struct Point { - pub x: f64, - pub y: f64, -} - -impl Point { - pub fn new(x: f64, y: f64) -> Self { - Self { x, y } - } - - pub fn add(&self, other: &Point) -> Point { - Point::new(self.x + other.x, self.y + other.y) - } -} - /// Represents a scope with current drawing state. #[derive(Debug, Clone)] pub struct Scope { pub uniting: bool, pub width: f64, - pub position: Point, + pub position: kurbo::Point, pub is_filled: bool, } @@ -29,7 +12,7 @@ impl Scope { Self { uniting: true, width: 1.0, - position: Point::new(0.0, 0.0), + position: kurbo::Point::new(0.0, 0.0), is_filled: false, } } From a474c138d484955daf10546ad9eaff70343dda94 Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Thu, 22 Jan 2026 13:53:21 +0400 Subject: [PATCH 3/7] [#9] Get rid of `lf` command --- README.md | 31 ++++++++++--------- grammar/IconScript.g4 | 2 +- js/src/parser.ts | 4 +-- .../syntax/iconscript.tmLanguage.json | 2 +- js/syntax/iconscript.sublime-syntax | 2 +- js/syntax/iconscript.vim | 2 +- rust/src/listener.rs | 4 +-- 7 files changed, 22 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 5545f2c..9037016 100644 --- a/README.md +++ b/README.md @@ -25,24 +25,25 @@ Syntax slightly resembles the syntax of path commands in SVG. ### Global context - - `width` — stroke width. - - `position` — current position of the cursor. + - `width` — float, stroke width. + - `position` — vector (float, float), current position of the cursor. + - `fill` -- Boolean, whether objects should be filled. ### Commands `` is 2D coordinates in the form `,` or `+,` (`+` means that the position is relative to the __position__). -| Command | Description | -| ------------------------------------ | --------------------------------------------------------------------------------- | -| `subtract` | Set subtraction mode | -| `w ` | Set `width` to a value | -| `m ` | Set `position` to a value | -| `l []` | Draw lines between positions | -| `lf []` | Draw filled lines between positions | -| `e ` | Draw circle specified by center point and radius | -| `r ` | Draw rectangle specified by top left and bottom right points | -| `a ` | Draw arc specified by center point, radius, start angle, and end angle in radians | +| Command | Description | +| ----------------------------------------------- | ------------------------- | +| `subtract` | Set subtraction mode | +| `fill` | Set fill mode | +| `m ` | Set `position` to a value | +| `w ` | Set `width` to a value | +| `l []` | Draw polyline | +| `r ` | Draw rectangle | +| `e
` | Draw circle | +| `a
` | Draw arc | ### Variables @@ -57,12 +58,12 @@ used to incapsulate context changes. ### Example ```iconscript -square = lf +0,0 +2,0 +0,2 +-2,0 +0,-2 +square = {fill r +0,0 +2,2} icon glider = { m 6,2 @square m +4,4 @square m +-8,4 @square m +4,0 @square m +4,0 @square } ``` -This code defines a square (`lf`, filled line — polygon with 5 points). It then -reuses `square` variable 5 times to draw a glider. +This code defines a square (filled rectangle). It then reuses `square` variable +5 times to draw a glider. diff --git a/grammar/IconScript.g4 b/grammar/IconScript.g4 index 284a923..d1dfc43 100644 --- a/grammar/IconScript.g4 +++ b/grammar/IconScript.g4 @@ -34,7 +34,7 @@ command // Figures. arc : 'a' position FLOAT FLOAT FLOAT ; circle : 'e' position FLOAT ; -line : ('l' | 'lf') position+ ; +line : 'l' position+ ; rectangle : 'r' position position ; // Icon name. diff --git a/js/src/parser.ts b/js/src/parser.ts index 0a1faa2..2142f29 100644 --- a/js/src/parser.ts +++ b/js/src/parser.ts @@ -347,8 +347,6 @@ class IconScriptListener extends GeneratedIconScriptListener { }; exitLine = (ctx: LineContext): void => { - const isFilled = - ctx.getText().includes("lf") || this.getScope().isFilled; const positions = ctx.position_list(); const coordinates: Point[] = []; @@ -387,7 +385,7 @@ class IconScriptListener extends GeneratedIconScriptListener { } // If filled, add a filled polyline. - if (isFilled && coordinates.length >= 2) { + if (this.getScope().isFilled && coordinates.length >= 2) { let filledPath = `M ${coordinates[0].x} ${coordinates[0].y}`; for (let i = 1; i < coordinates.length; i++) { filledPath += ` L ${coordinates[i].x} ${coordinates[i].y}`; diff --git a/js/syntax/iconscript-vscode/syntax/iconscript.tmLanguage.json b/js/syntax/iconscript-vscode/syntax/iconscript.tmLanguage.json index 5170ba4..b374114 100644 --- a/js/syntax/iconscript-vscode/syntax/iconscript.tmLanguage.json +++ b/js/syntax/iconscript-vscode/syntax/iconscript.tmLanguage.json @@ -22,7 +22,7 @@ }, "commands": { "name": "keyword.other.iconscript", - "match": "\\b(l|lf|c|s|a|p|w|r)\\b" + "match": "\\b(l|c|s|a|p|w|r)\\b" }, "variables": { "name": "variable.other.readwrite.iconscript", diff --git a/js/syntax/iconscript.sublime-syntax b/js/syntax/iconscript.sublime-syntax index 1ad0ab1..c3c5cbd 100644 --- a/js/syntax/iconscript.sublime-syntax +++ b/js/syntax/iconscript.sublime-syntax @@ -6,7 +6,7 @@ contexts: main: - match: '\\[\\{}:]' scope: markup.raw - - match: '\b(c|p|l|lf|w)\b' + - match: '\b(c|p|l|w)\b' scope: keyword - match: "@[a-zA-Z0-9_]*" scope: variable.member diff --git a/js/syntax/iconscript.vim b/js/syntax/iconscript.vim index 1db49b1..b200120 100644 --- a/js/syntax/iconscript.vim +++ b/js/syntax/iconscript.vim @@ -9,7 +9,7 @@ syn case match syn match iconscriptComment "^#.*$" syn keyword iconscriptKeyword icon -syn keyword iconscriptCommand l lf c s a p w r +syn keyword iconscriptCommand l c s a p w r syn match iconscriptVariable "@[a-zA-Z_][a-zA-Z0-9_]*" syn match iconscriptNumber "-\?\d\+\(\.\d*\)\?" syn match iconscriptPosition "\+\?\d\+\(\.\d*\)\?,\s*\d\+\(\.\d*\)\?" diff --git a/rust/src/listener.rs b/rust/src/listener.rs index 2e92a94..aff1868 100644 --- a/rust/src/listener.rs +++ b/rust/src/listener.rs @@ -237,8 +237,6 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { } fn exit_line(&mut self, ctx: &LineContext<'input>) { - let is_filled = - ctx.get_text().contains("lf") || self.get_scope().is_filled; let positions: Vec = ctx .position_all() .iter() @@ -276,7 +274,7 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { } // If filled, add a filled polyline. - if is_filled && positions.len() >= 2 { + if self.get_scope_mut().is_filled && positions.len() >= 2 { let mut path = format!("M {} {}", positions[0].x, positions[0].y); for pos in &positions[1..] { path.push_str(&format!(" L {} {}", pos.x, pos.y)); From 674eb5bb435d35ccf76d5326b462cf9fe296d834 Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Thu, 22 Jan 2026 15:51:40 +0400 Subject: [PATCH 4/7] [#9] Unify `e` command --- js/src/parser.ts | 24 +++++++++++++++++++++--- rust/src/generator.rs | 2 +- rust/src/listener.rs | 15 ++++++++++++++- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/js/src/parser.ts b/js/src/parser.ts index 2142f29..05d65f7 100644 --- a/js/src/parser.ts +++ b/js/src/parser.ts @@ -399,12 +399,30 @@ class IconScriptListener extends GeneratedIconScriptListener { exitCircle = (ctx: CircleContext): void => { const center = this.getScope().getPosition(ctx.position()); const radius = parseFloat(ctx.FLOAT().getText()); - const circlePath = createCirclePath(center.x, center.y, radius / 2); - if (circlePath) { - this.paths.push(circlePath); + const radiusOuter = radius + this.getScope().width / 2.0; + const radiusInner = radius + this.getScope().width / 2.0; + + const circlePathOuter = createCirclePath( + center.x, + center.y, + radiusOuter, + ); + if (circlePathOuter) { + this.paths.push(circlePathOuter); this.modes.push(this.getScope().uniting); } + if (this.getScope().isFilled && radiusInner > 0.0) { + const circlePathInner = createCirclePath( + center.x, + center.y, + radiusInner, + ); + if (circlePathInner) { + this.paths.push(circlePathInner); + this.modes.push(this.getScope().uniting); + } + } }; exitArc = (ctx: ArcContext): void => { diff --git a/rust/src/generator.rs b/rust/src/generator.rs index 9dfe6f3..5914efb 100644 --- a/rust/src/generator.rs +++ b/rust/src/generator.rs @@ -27,7 +27,7 @@ impl Default for OptimizationOptions { /// Create a circle path using SVG arc commands. pub fn create_circle_path(cx: f64, cy: f64, r: f64) -> Option { if !cx.is_finite() || !cy.is_finite() || !r.is_finite() { - eprintln!("Invalid circle coordinates: ({}, {}, {})", cx, cy, r); + eprintln!("Invalid circle coordinates: ({}, {}, {}).", cx, cy, r); return None; } diff --git a/rust/src/listener.rs b/rust/src/listener.rs index aff1868..603247c 100644 --- a/rust/src/listener.rs +++ b/rust/src/listener.rs @@ -225,14 +225,27 @@ impl<'input> IconScriptListener<'input> for IconScriptListenerImpl<'input> { let radius_text = ctx.FLOAT().unwrap().get_text(); let radius: f64 = radius_text.parse().unwrap_or(0.0); + let radius_outer: f64 = radius + self.get_scope_mut().width / 2.0; + let radius_inner: f64 = radius - self.get_scope_mut().width / 2.0; + if let Some(path) = - create_circle_path(center.x, center.y, radius / 2.0) + create_circle_path(center.x, center.y, radius_outer) { self.paths.push(PathWithMode { path, mode: self.get_scope().uniting, }); } + if !self.get_scope_mut().is_filled && radius_inner > 0.0 { + if let Some(path) = + create_circle_path(center.x, center.y, radius_inner) + { + self.paths.push(PathWithMode { + path, + mode: !self.get_scope().uniting, + }); + } + } } } From b6fc3e165435d874278cc072c31554a94f16bcd0 Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Thu, 22 Jan 2026 16:35:37 +0400 Subject: [PATCH 5/7] [#9] Update syntax in tests --- js/test/flag.iconscript | 71 ++++++++++++++++++++++++----------------- js/test/main.iconscript | 41 +++++++++++++----------- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/js/test/flag.iconscript b/js/test/flag.iconscript index db533e9..521a438 100644 --- a/js/test/flag.iconscript +++ b/js/test/flag.iconscript @@ -1,45 +1,53 @@ flag = l 1,13 1,3 13,3 13,11 1,11 -flag_fill = lf 1,13 1,3 13,3 13,11 1,11 -triangle_lf = lf 1,3 7,7 1,11 +flag_fill = {fill l 1,13 1,3 13,3 13,11 1,11} +triangle_lf = {fill l 1,3 7,7 1,11} line = l +0,0 +12,0 line_h = l +0,0 +0,8 -line_w = lf +0,0 +12,0 +0,2 +-12,0 +line_w = {fill l +0,0 +12,0 +0,2 +-12,0} icon flag_solid = {@flag_fill} -icon flag_canton = {@flag lf 1,3 +6,0 +0,4 +-6,0} +icon flag_canton = {@flag fill l 1,3 +6,0 +0,4 +-6,0} icon flag_usa = { - @flag m 1,5 @line m 1,7 @line m 1,9 @line r 1,3 +6,4 - {subtract e 2,4 1 e 4,4 1 e 6,4 1 e 2,6 1 e 4,6 1 e 6,6 1} + @flag m 1,5 @line m 1,7 @line m 1,9 @line + fill r 1,3 +6,4 + { + subtract w 0 + e 2,4 0.5 e 4,4 0.5 e 6,4 0.5 e 2,6 0.5 e 4,6 0.5 e 6,6 0.5 + } } icon flag_triangle_flanche = {@flag @triangle_lf} icon flag_symmetric_cross = {@flag m 1,7 @line m 7,3 @line_h} icon flag_saltire = {@flag l 1,3 13,11 l 1,11 13,3} -icon flag_horizontal_bisection = {@flag lf 1,3 +12,0 +0,4 +-12,0} +icon flag_horizontal_bisection = {@flag fill l 1,3 +12,0 +0,4 +-12,0} icon flag_horizontal_triband = {@flag m 1,6 @line_w} -icon flag_vertical_bisection = {@flag lf 1,3 +6,0 +0,8 +-6,0} -icon flag_vertical_triband = {@flag r 5,3 +4,8} +icon flag_vertical_bisection = {@flag fill l 1,3 +6,0 +0,8 +-6,0} +icon flag_vertical_triband = {@flag fill r 5,3 +4,8} icon flag_stripes = {@flag m 1,5 @line m 1,7 @line m 1,9 @line} -icon flag_bend_dexter = {@flag lf 2,3 13,10 12,11 1,4} -icon flag_bend_sinister = {@flag lf 1,10 12,3 13,4 2,11} -icon flag_quadrisection = {@flag r 1,3 +6,4 r 7,7 +6,4} +icon flag_bend_dexter = {@flag fill l 2,3 13,10 12,11 1,4} +icon flag_bend_sinister = {@flag fill l 1,10 12,3 13,4 2,11} +icon flag_quadrisection = {@flag fill r 1,3 +6,4 r 7,7 +6,4} icon flag_gusset = {@flag @triangle_lf m 1,6 @line_w} -icon flag_diagonal_bisection_sinister = {@flag lf 1,3 +12,0 +-12,8} -icon flag_diagonal_bisection = {@flag lf 1,3 +12,8 +0,-8} +icon flag_diagonal_bisection_sinister = {@flag fill l 1,3 +12,0 +-12,8} +icon flag_diagonal_bisection = {@flag fill l 1,3 +12,8 +0,-8} icon flag_europe = { @flag_fill { - subtract m 7,4 - e +0,0 1 e +2,1 1 e +1,2 1 e +-1,2 1 - e +-2,1 1 e +-2,-1 1 e +-1,-2 1 e +1,-2 1 + subtract m 7,4 fill w 0 + e +0,0 0.5 e +2,1 0.5 e +1,2 0.5 e +-1,2 0.5 + e +-2,1 0.5 e +-2,-1 0.5 e +-1,-2 0.5 e +1,-2 0.5 } } icon flag_philippines = { - @flag @triangle_lf m 1,7 @line {subtract e 3,7 1.41 e 2,9 1 e 2,5 1 e 5,7 1} + @flag @triangle_lf m 1,7 @line + {subtract fill w 0 e 3,7 0.707 e 2,9 0.5 e 2,5 0.5 e 5,7 0.5} } icon flag_china = { - @flag_fill {subtract e 3,6 1.41 e 4.5,8 1 e 4.5,4 1 e 6,7 1 e 6,5 1} + @flag_fill { + subtract fill w 0 + e 3,6 0.707 e 4.5,8 0.5 e 4.5,4 0.5 e 6,7 0.5 e 6,5 0.5 + } } icon flag_horizontal_triband_light_dark_light_dot = { - @flag m 1,9 @line_w m 1,3 @line_w e 7,7 1.41 + @flag m 1,9 @line_w m 1,3 @line_w fill w 0 e 7,7 0.707 } icon flag_nordic_cross = {@flag m 1,7 @line m 5,3 @line_h} icon flag_nordic_cross_v1 = { @@ -51,22 +59,27 @@ icon flag_nordic_cross_double = { m 1,7 @line m 5,3 @line_h @flag } -icon flag_canada = {@flag r 1,3 +3,8 r 10,3 +3,8 e 7,7 3} -icon flag_chile = {@flag r 1,7 13,11 r 1,3 5,7 {subtract e 3,5 1.41 }} +icon flag_canada = {@flag fill r 1,3 +3,8 r 10,3 +3,8 w 0 e 7,7 1.5} +icon flag_chile = {@flag fill r 1,7 13,11 r 1,3 5,7 {subtract w 0 e 3,5 0.707}} icon flag_chicago = { @flag m 1,5 @line m 1,9 @line - e 4,7 1.41 e +2,0 1.41 e +2,0 1.41 e +2,0 1.41 + fill w 0 + e 4,7 0.707 e +2,0 0.707 e +2,0 0.707 e +2,0 0.707 } icon flag_spain = { - @flag m 1,9 @line_w m 1,3 @line_w e 4,7 1.41 + @flag m 1,9 @line_w m 1,3 @line_w fill w 0 e 4,7 0.707 } icon flag_australia = { - @flag r 1,7 13,11 r 7,3 +5,4 l 1,5 +6,0 l 4,3 +0,4 - {subtract e 8,6 1 e +2,-2 1 e +1.5,2 1 e +-1,1.5 1 e +-0.5,2.5 1 e 4,9 1.41} + @flag fill r 1,7 13,11 r 7,3 +5,4 l 1,5 +6,0 l 4,3 +0,4 + { + subtract w 0 + e 8,6 0.5 e +2,-2 0.5 e +1.5,2 0.5 + e +-1,1.5 0.5 e +-0.5,2.5 0.5 e 4,9 0.707 + } } -icon flag_circle = {@flag_fill {subtract e 7,7 5}} -icon flag_circle_alt = {@flag e 7,7 5} -icon flag_turkmenistan = {@flag r 4,3 +1,8 e 7,5 1} +icon flag_circle = {@flag_fill {subtract fill w 0 e 7,7 2.5}} +icon flag_circle_alt = {@flag fill w 0 e 7,7 2.5} +icon flag_turkmenistan = {@flag fill r 4,3 +1,8 w 0 e 7,5 0.5} icon flag_saltire_symmetric_cross = { @flag m 1,7 @line m 7,3 @line_h l 1,3 13,11 l 1,11 13,3 } diff --git a/js/test/main.iconscript b/js/test/main.iconscript index 75de2fd..d12a2a1 100644 --- a/js/test/main.iconscript +++ b/js/test/main.iconscript @@ -1,18 +1,18 @@ icon cross_catholic_3 = { - lf 6,3 +0,3 +-3,0 +0,2 +3,0 +0,5 +2,0 +0,-5 +3,0 +0,-2 +-3,0 +0,-3 +-2,0 + fill l 6,3 +0,3 +-3,0 +0,2 +3,0 +0,5 +2,0 +0,-5 +3,0 +0,-2 +-3,0 +0,-3 +-2,0 } icon star_of_david = { l 7,2 +-5,8 +10,0 +-5,-8 l 7,13 +-5,-8 +10,0 +-5,8 } icon crane_portal_v3 = { - lf 3,7 +3,0 +1,1 +0,2 +-5,0 +0,-2 +1,-1 + {fill l 3,7 +3,0 +1,1 +0,2 +-5,0 +0,-2 +1,-1} l 3,10 +-1,2 +0,2 l 6,10 +1,2 +0,2 l 3,7 9,1 l 6,7 9,1 +2,6 } icon crane_v3 = { - lf 2,2 +5,0 +1,1 +5,0 +-11,0 +0,-1 + {fill l 2,2 +5,0 +1,1 +5,0 +-11,0 +0,-1} l 4,3 +0,11 l 6,3 +0,11 l 4,3 +2,2 @@ -22,13 +22,14 @@ icon crane_v3 = { l 11,3 +0,5 } icon button = { - e 7,7 9 {subtract e 6,6 1.3 e 8,8 1.3 e 8,6 1.3 e 6,8 1.3} + fill w 0 + e 7,7 4.5 {subtract e 6,6 0.707 e 8,8 0.707 e 8,6 0.707 e 6,8 0.707} } icon manhole_v1 = { - e 7,7 9 {subtract l 6,4 +2,0 l 4,6 +6,0 l 4,8 +6,0 l 6,10 +2,0} + {fill w 0 e 7,7 4.5} {subtract l 6,4 +2,0 l 4,6 +6,0 l 4,8 +6,0 l 6,10 +2,0} } person = - e +0,0 2 l +-3,2 +6,0 r +-5,0 +4,3 + {fill w 0 e +0,0 1 l +-3,2 +6,0 r +-5,0 +4,3} {subtract r +-5,-2 +1,4 r +4,-4 +1,4 l +-3,-2 +0,4} l +-3,-1 +0,2 l +2,-2 +0,2 @@ -41,43 +42,45 @@ icon smoke_2 = { icon old_tv = { r 2,3 +8,7 {subtract r +-7,-5 +4,3 l +2,-3 +0,2} - e 6,3 3 + {fill w 0 e 6,3 1.5} l +0,0 +-3,-3 l +6,0 +-3,3 } icon ice_cream_3_scoops_v0 = { - e 7.5,3.5 4 e 5.5,5.5 4 e 9.5,5.5 4 - lf 7.5,5 +2,2 +-2,6 +-2,-6 {subtract l 3,7 +9,0} + {fill w 0 e 7.5,3.5 2 e 5.5,5.5 2 e 9.5,5.5 2} + {fill l 7.5,5 +2,2 +-2,6 +-2,-6} + {subtract l 3,7 +9,0} } icon ice_cream_2_scoops_v0 = { - e 6,5.5 4 e 9,5.5 4 lf 7.5,5 +2,2 +-2,6 +-2,-6 {subtract l 3,7 +9,0} + {fill w 0 e 6,5.5 2 e 9,5.5 2} {fill l 7.5,5 +2,2 +-2,6 +-2,-6} + {subtract l 3,7 +9,0} } icon ice_cream = { - e 7.5,3.5 6 r 5,3.5 +5,6.5 {w 2 l 7.5,10.5 +0,3} + {fill w 0 e 7.5,3.5 3} r 5,3.5 +5,6.5 {w 2 l 7.5,10.5 +0,3} {subtract {w 2 l 6.5,5.5 +0,2} l 9,7 +0,1 {w 0 r 5.5,7.5 +4,2}} l 8,7 +0,1 } icon couch = { {w 2 r 2.5,4.5 +9,5} - {subtract e 1.5,7.5 4 e 12.5,7.5 4 l 1,8 +12,0} - e 1.5,7.5 2 e 12.5,7.5 2 + {subtract fill w 0 e 1.5,7.5 2 e 12.5,7.5 2 l 1,8 +12,0} + {fill w 0 e 1.5,7.5 1 e 12.5,7.5 1} r 2,9 +10,1 l 2,9 +0,-1.5 l 12,9 +0,-1.5 l 2,10 +0,1 l 12,10 +0,1 } icon temp = { - e 7,6 7 + {fill w 0 e 7,6 3.5} a 7,6 5 -1.97 1.16 l 7,11 +0,2 l +-3,0 +6,0 } -icon temp = {lf 5,11 +0,-9 +5,3 +-5,3} +icon temp = {fill l 5,11 +0,-9 +5,3 +-5,3} -triangle_ul = lf +0,-3 +3,5 +-6,0 +3,-5 m +0,3 -triangle_us = lf +0,-2 +2,3 +-4,0 +2,-3 m +0,2 -triangle_dl = lf +0,3 +3,-5 +-6,0 +3,5 m +0,-3 -triangle_ds = lf +0,2 +2,-3 +-4,0 +2,3 m +0,-2 +triangle_ul = fill l +0,-3 +3,5 +-6,0 +3,-5 m +0,3 +triangle_us = fill l +0,-2 +2,3 +-4,0 +2,-3 m +0,2 +triangle_dl = fill l +0,3 +3,-5 +-6,0 +3,5 m +0,-3 +triangle_ds = fill l +0,2 +2,-3 +-4,0 +2,3 m +0,-2 icon temp = {m 7,7 @triangle_ul} icon temp = {m 7,7 @triangle_us} From 0a5f092933a241eff49142cdff59ec7ac33b498f Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Fri, 23 Jan 2026 01:11:02 +0400 Subject: [PATCH 6/7] [#9] Update syntax documentation --- {js/doc => doc}/diagram.md | 0 {js/doc => doc}/public.moi | 8 +++-- doc/syntax.moi | 62 ++++++++++++++++++++++++++++++++++++++ js/doc/syntax.moi | 55 --------------------------------- js/src/parser.ts | 6 ++-- js/web/style.css | 18 +++++++++-- 6 files changed, 87 insertions(+), 62 deletions(-) rename {js/doc => doc}/diagram.md (100%) rename {js/doc => doc}/public.moi (83%) create mode 100644 doc/syntax.moi delete mode 100644 js/doc/syntax.moi diff --git a/js/doc/diagram.md b/doc/diagram.md similarity index 100% rename from js/doc/diagram.md rename to doc/diagram.md diff --git a/js/doc/public.moi b/doc/public.moi similarity index 83% rename from js/doc/public.moi rename to doc/public.moi index dc89820..d5fce6b 100644 --- a/js/doc/public.moi +++ b/doc/public.moi @@ -4,8 +4,12 @@ \javascript {iconscript-parser.bundle.min.js} \javascript {iconscript-ui.bundle.min.js} -iconscript is a simple language for describing simple pixel-wise pictograms in -the style of the \ref {#roentgen} {Röntgen} project. +iconscript is a language for describing pixel-wise pictograms in the style of +the \ref {#roentgen} {Röntgen} project implemented in Rust and TypeScript. You +can install it with: +\list + {\c {cargo install iconscript }— fast Rust implementation,} + {\c {npm install iconscript }— TypeScript implementation used on this page.} \html {
diff --git a/doc/syntax.moi b/doc/syntax.moi new file mode 100644 index 0000000..8b95718 --- /dev/null +++ b/doc/syntax.moi @@ -0,0 +1,62 @@ +\2 {Syntax} {iconscript_syntax} + +Syntax slightly resembles the syntax of path commands in \abbr {SVG}. Positions +on the plane are coded as two floating point number separated by comma: +\c {\formal {x},\formal {y}} or \c {+\formal {x},\formal {y}}. +\c {+} means that the position is relative to the \c {position}. + +If current position is (5, 5), `+1,2` will give (6, 7) and update current +position to (6, 7) as well. + +\3 {Global context} {iconscript_global_context} + +\list + {\c {position} — vector of two floats, current position of the cursor.} + {\c {width} — float, stroke width.} + {\c {fill} — Boolean, whether objects should be filled.} + +\3 {Commands} {iconscript_commands} + +\table + {{\b {Command}} {\b {Description}}} + {{\c {\skw {subtract}}} {Set subtraction mode}} + {{\c {\skw {fill}}} {Set fill mode}} + {{\c {\skw {w} \formal {float}}} {Set \c {width} to a value}} + {{\c {\skw {m} \formal {position}}} {Set \c {position} to a value}} + {{\c {\skw {l} [\formal {position}]}} {Draw lines between positions}} + { + {\c {\skw {e} \formal {position} \formal {float}}} + {Draw circle specified by center point and radius} + } + { + {\c {\skw {r} \formal {position} \formal {position}}} + {Draw rectangle specified by top left and bottom right points} + } + { + {\c {\skw {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 { = []} and accessed with +\c {@}. + +\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} { +square = \skw {fill} \skw{r} +0,0 +2,2 +\skw {icon} glider = \{ + \skw {m} 6,2 @square \skw {m} +4,4 @square + \skw {m} +-8,4 @square \skw {m} +4,0 @square \skw {m} +4,0 @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. \ No newline at end of file diff --git a/js/doc/syntax.moi b/js/doc/syntax.moi deleted file mode 100644 index 67a31a9..0000000 --- a/js/doc/syntax.moi +++ /dev/null @@ -1,55 +0,0 @@ -\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 { = []} and accessed with -\c {@}. - -\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. \ No newline at end of file diff --git a/js/src/parser.ts b/js/src/parser.ts index 05d65f7..1c573b7 100644 --- a/js/src/parser.ts +++ b/js/src/parser.ts @@ -401,7 +401,7 @@ class IconScriptListener extends GeneratedIconScriptListener { const radius = parseFloat(ctx.FLOAT().getText()); const radiusOuter = radius + this.getScope().width / 2.0; - const radiusInner = radius + this.getScope().width / 2.0; + const radiusInner = radius - this.getScope().width / 2.0; const circlePathOuter = createCirclePath( center.x, @@ -412,7 +412,7 @@ class IconScriptListener extends GeneratedIconScriptListener { this.paths.push(circlePathOuter); this.modes.push(this.getScope().uniting); } - if (this.getScope().isFilled && radiusInner > 0.0) { + if (!this.getScope().isFilled && radiusInner > 0.0) { const circlePathInner = createCirclePath( center.x, center.y, @@ -420,7 +420,7 @@ class IconScriptListener extends GeneratedIconScriptListener { ); if (circlePathInner) { this.paths.push(circlePathInner); - this.modes.push(this.getScope().uniting); + this.modes.push(!this.getScope().uniting); } } }; diff --git a/js/web/style.css b/js/web/style.css index 70dcb51..6d14f1f 100644 --- a/js/web/style.css +++ b/js/web/style.css @@ -2,12 +2,26 @@ --border-opacity: 0.1; } body { - font-family: sans; + font-family: Inter; margin: 0; } +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: Inter; +} +h1 { + font-size: 72pt; + font-variation-settings: + "opsz" 32, + "wght" 700; +} .container { display: flex; - margin: 0; + margin: 25px 0; border-top: 1px solid rgba(var(--fg-color), var(--border-opacity)); border-bottom: 1px solid rgba(var(--fg-color), var(--border-opacity)); } From 25e4c9a055579f044747d159857f78f45c7172d0 Mon Sep 17 00:00:00 2001 From: Sergey Vartanov Date: Fri, 23 Jan 2026 01:13:57 +0400 Subject: [PATCH 7/7] [#9] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8effe31..fe8777b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# Unreleased + + - **Breaking change**: unify syntax + ([#2](https://github.com/enzet/iconscript/issues/9)). + - Add `fill` command. + - Remove `lf` command. + - Make `r` and `e` respect fill and width scope parameters. + # 0.2.0 - **Breaking change**: use SVG path commands format