1+ /*
2+ * Copyright (c) 2021 ByteSkript org (Moderocky)
3+ * View the full licence information and permissions:
4+ * https://github.com/Moderocky/ByteSkript/blob/master/LICENSE
5+ */
6+
7+ package org .byteskript .skript .compiler ;
8+
9+ import org .jetbrains .annotations .NotNull ;
10+ import org .jetbrains .annotations .Nullable ;
11+
12+ import java .util .*;
13+ import java .util .concurrent .atomic .AtomicReference ;
14+ import java .util .function .Function ;
15+ import java .util .function .Supplier ;
16+ import java .util .stream .IntStream ;
17+ import java .util .stream .Stream ;
18+
19+ import static org .byteskript .skript .compiler .NewPattern .Node .*;
20+
21+ public class NewPattern {
22+ public static void main (final String [] args ) {
23+ final Node elementPattern = recurse (self -> input ("value" , either (text ("," , either (text (" " , self ), self )), text (")" , end ()))));
24+ final Node listPattern = text ("(" , elementPattern );
25+ {
26+ final Resolved match = match ("(1, 2, 3, 4)" , listPattern );
27+ if (match != null ) {
28+ System .out .println ("List is:" );
29+ for (final String input : match .group ("value" )) {
30+ System .out .println (" - " + input );
31+ }
32+ }
33+ }
34+
35+ {
36+ final Node twoListsPattern = labelledChild ("one" , listPattern , text (" and " , labelledChild ("two" , listPattern , end ())));
37+ final Resolved twoMatch = match ("(1,2,3) and (4, 5, 6)" , twoListsPattern );
38+ if (twoMatch != null ) {
39+ System .out .println ("List 1 is:" );
40+ for (final String input : twoMatch .group ("one.value" )) {
41+ System .out .println (" - " + input );
42+ }
43+ System .out .println ("List 2 is:" );
44+ for (final String input : twoMatch .group ("two.value" )) {
45+ System .out .println (" - " + input );
46+ }
47+ }
48+ }
49+
50+ final Node digit = oneOf (text ("0" ), text ("1" ), text ("2" ), text ("3" ), text ("4" ), text ("5" ), text ("6" ), text ("7" ), text ("8" ), text ("9" ));
51+ final Node naturalNumber = label ("value" , recurse (self -> oneOf (digit , child (digit , self ))));
52+ final Resolved natural = match ("69" , naturalNumber );
53+ final Node integer = label ("value" , optional ("-" , labelledChild ("natural" , naturalNumber , end ())));
54+ final Node decimal = label ("value" , oneOf (naturalNumber , child (naturalNumber , text ("." , naturalNumber ))));
55+ {
56+
57+ if (natural != null ) System .out .println ("Natural is " + natural .group ("value" ).get (0 ));
58+ {
59+ final Resolved intMatch = match ("-67" , integer );
60+ if (intMatch != null ) System .out .println ("Integer is " + intMatch .group ("value" ).get (0 ));
61+ }
62+ {
63+ final Resolved decMatch = match ("67.80085" , decimal );
64+ if (decMatch != null ) System .out .println ("Decimal is " + decMatch .group ("value" ).get (0 ));
65+ }
66+ }
67+
68+ {
69+ final Node statement = maybeWhitespace (oneOf (
70+ labelledChild ("statement" , text ("print" , maybeWhitespace (label ("value" , integer ))), maybeWhitespace (text (";" ))),
71+ labelledChild ("statement" , text ("hi" ), maybeWhitespace (text (";" ))),
72+ labelledChild ("statement" , text ("hiya" ), maybeWhitespace (text (";" )))
73+ ));
74+ final Node block = text ("{" , maybeWhitespace (child (recurse (self -> oneOf (statement , child (statement , self ))), maybeWhitespace (text ("}" )))));
75+ final Node function = text ("function " , identifier ("name" , maybeWhitespace (block )));
76+ final Resolved resolved = match ("""
77+ function foo {
78+ hi;
79+ print -5;
80+ hiya;
81+ }""" , function );
82+
83+ if (resolved != null ) {
84+ System .out .println ("Function is named " + resolved .group ("name" ));
85+ for (final String line : resolved .group ("statement" , true )) {
86+ System .out .println (" - " + line );
87+ }
88+ }
89+ }
90+ }
91+
92+ private static Resolved match (final String input , final Node pattern ) {
93+ return matches (0 , input , pattern ).max (Comparator .naturalOrder ()).orElse (null );
94+ }
95+
96+ interface Context {
97+ Node redirect ();
98+ String name ();
99+ }
100+
101+ record ChildContext (ChildNode node , Node redirect ) implements Context {
102+ public Context parent () {
103+ return node .context ();
104+ }
105+
106+ @ Override
107+ public String name () {
108+ return (parent () != null ? parent ().name () + "." : "" ) + (node .label () != null ? node .label () : "" );
109+ }
110+ }
111+
112+ sealed interface Node permits Branch , Dynamic , Labelled , Literal , Terminal {
113+ Context context ();
114+
115+ Set <Node > children ();
116+
117+ static Literal text (final String text , final Node then ) {
118+ return new Literal (text , then );
119+ }
120+
121+ static Node optional (final String value , final Node then ) {
122+ return oneOf (text (value , then ), then );
123+ }
124+
125+ static Node maybeWhitespace (final Node then ) {
126+ final Node oneWhitespace = oneOf (text (" " ), text ("\n " ));
127+ return recurse (self -> oneOf (then , child (oneWhitespace , self )));
128+ }
129+
130+ static Literal text (final String text ) {
131+ return new Literal (text , end ());
132+ }
133+
134+ static Branch either (final Node one , final Node other ) {
135+ return new Branch (one , other );
136+ }
137+
138+ static Node oneOf (final Node first , final Node ... others ) {
139+ return Arrays .stream (others ).reduce (first , Branch ::new );
140+ }
141+
142+ static Dynamic recurse (final Function <Dynamic , Node > function ) {
143+ final AtomicReference <Dynamic > reference = new AtomicReference <>();
144+ reference .set (new Dynamic (() -> function .apply (reference .get ())));
145+ return reference .get ();
146+ }
147+
148+ static Identifier identifier (final Node then ) {
149+ return new Identifier (null , then );
150+ }
151+
152+ static Identifier identifier (final String name , final Node then ) {
153+ return new Identifier (name , then );
154+ }
155+
156+ static Input input (final String name , final Node then ) {
157+ return new Input (name , then );
158+ }
159+
160+ static ChildNode labelledChild (final String name , final Node subNode , final Node then ) {
161+ return new ChildNode (name , subNode , then );
162+ }
163+
164+ static ChildNode label (final String label , final Node subNode ) {
165+ return new ChildNode (label , subNode , end ());
166+ }
167+
168+ static ChildNode child (final Node subNode , final Node then ) {
169+ return new ChildNode (null , subNode , then );
170+ }
171+
172+ static Terminal end () {
173+ return new Terminal ();
174+ }
175+
176+ Node withContext (final Context context );
177+ }
178+
179+ sealed interface Labelled extends Node permits Input , Identifier , ChildNode {
180+ String label ();
181+ default String fullLabel () {
182+ return (context () != null ? context ().name () + "." : "" ) + label ();
183+ }
184+ }
185+
186+ record ChildNode (Context context , String label , Node rawNode , Node then ) implements Labelled {
187+ ChildNode (final String label , final Node rawNode , final Node then ) {
188+ this (null , label , rawNode , then );
189+ }
190+
191+ @ Override
192+ public Set <Node > children () {
193+ return Set .of (node ());
194+ }
195+
196+ public Node node () {
197+ return rawNode .withContext (new ChildContext (this , then ));
198+ }
199+
200+ @ Override
201+ public Node withContext (final Context context ) {
202+ final Node newThen = then .withContext (context );
203+ return new ChildNode (context , label , rawNode .withContext (new ChildContext (this , newThen )), newThen );
204+ }
205+ }
206+
207+ record Literal (Context context , String text , Node next ) implements Node {
208+ Literal (final String text , final Node next ) {
209+ this (null , text , next );
210+ }
211+
212+ @ Override
213+ public Set <Node > children () {
214+ return Set .of (next );
215+ }
216+
217+ @ Override
218+ public Node withContext (final Context context ) {
219+ return new Literal (context , text , next .withContext (context ));
220+ }
221+ }
222+
223+ record Terminal (Context context ) implements Node {
224+ Terminal () {
225+ this (null );
226+ }
227+
228+ @ Override
229+ public Set <Node > children () {
230+ return Collections .emptySet ();
231+ }
232+
233+ @ Override
234+ public Node withContext (final Context context ) {
235+ return new Terminal (context );
236+ }
237+ }
238+
239+ record Branch (Context context , Node primary , Node secondary ) implements Node {
240+ Branch (final Node primary , final Node secondary ) {
241+ this (null , primary , secondary );
242+ }
243+
244+ @ Override
245+ public Set <Node > children () {
246+ return Set .of (primary , secondary );
247+ }
248+
249+ @ Override
250+ public Node withContext (final Context context ) {
251+ return new Branch (context , primary .withContext (context ), secondary .withContext (context ));
252+ }
253+ }
254+
255+ record Input (Context context , @ Nullable String label , Node next ) implements Labelled {
256+ Input (final String label , final Node next ) {
257+ this (null , label , next );
258+ }
259+
260+ @ Override
261+ public Set <Node > children () {
262+ return Set .of (next );
263+ }
264+
265+ @ Override
266+ public Node withContext (final Context context ) {
267+ return new Input (context , label , next .withContext (context ));
268+ }
269+ }
270+
271+ record Identifier (Context context , @ Nullable String label , Node next ) implements Labelled {
272+ Identifier (final String label , final Node next ) {
273+ this (null , label , next );
274+ }
275+
276+ @ Override
277+ public Set <Node > children () {
278+ return Set .of (next );
279+ }
280+
281+ @ Override
282+ public Node withContext (final Context context ) {
283+ return new Identifier (context , label , next .withContext (context ));
284+ }
285+ }
286+
287+ record Dynamic (Context context , Supplier <Node > supplier ) implements Node {
288+ Dynamic (final Supplier <Node > supplier ) {
289+ this (null , supplier );
290+ }
291+
292+ @ Override
293+ public Set <Node > children () {
294+ return supplier .get ().children ();
295+ }
296+
297+ @ Override
298+ public Node withContext (final Context context ) {
299+ return new Dynamic (context , () -> supplier .get ().withContext (context ));
300+ }
301+ }
302+
303+ record MatchElement (Node node , int start , int end ) {
304+ @ Override
305+ public @ NotNull String toString () {
306+ return (node .context () != null ? node .context ().name () + "." : "" ) + node .getClass ().getSimpleName () + "[" + start + "--" + end + "]" ;
307+ }
308+ }
309+
310+ record Resolved (List <MatchElement > match , String input ) implements Comparable <Resolved > {
311+ public Resolved prepend (final Node element , final int start , final String extraInput ) {
312+ final List <MatchElement > newList = new ArrayList <>(match .size () + 1 );
313+ newList .add (new MatchElement (element , start , start + extraInput .length ()));
314+ newList .addAll (match );
315+ return new Resolved (newList , extraInput + input );
316+ }
317+
318+ @ Override
319+ public int compareTo (@ NotNull final NewPattern .Resolved o ) {
320+ if (this .match .size () != o .match .size ()) return this .match .size () - o .match .size ();
321+ else return this .input .compareTo (o .input );
322+ }
323+
324+ public List <String > group (final String name , final boolean ignoreContext ) {
325+ return match .stream ()
326+ .filter (element -> element .node () instanceof final Labelled labelled && name .equals (ignoreContext ? labelled .label () : labelled .fullLabel ()))
327+ .map (elem -> input .substring (elem .start (), elem .end ()))
328+ .toList ();
329+ }
330+
331+ public List <String > group (final String name ) {
332+ return group (name , false );
333+ }
334+
335+ public Resolved tag (final Node node , final int start , final int end ) {
336+ final List <MatchElement > newList = new ArrayList <>(match .size () + 1 );
337+ newList .add (new MatchElement (node , start , end ));
338+ newList .addAll (match );
339+ return new Resolved (newList , this .input );
340+ }
341+
342+ public int startIndexOf (final Node node ) {
343+ return match .stream ().filter (e -> e .node .equals (node )).map (MatchElement ::start ).findFirst ().orElse (-1 );
344+ }
345+
346+ public int endIndexOf (final Node node ) {
347+ return match .stream ().filter (e -> e .node .equals (node )).map (MatchElement ::end ).findFirst ().orElse (-1 );
348+ }
349+ }
350+
351+ static Stream <Resolved > matches (final int start , final String text , final Node root ) {
352+ if (root instanceof final Literal literal ) {
353+ if (!text .startsWith (literal .text ())) return Stream .empty ();
354+ return literal .children ().stream ()
355+ .flatMap (child -> matches (start + literal .text ().length (), text .substring (literal .text ().length ()), child ))
356+ .map (v -> v .prepend (literal , start , literal .text ()));
357+ } else if (root instanceof final Identifier identifier ) {
358+ if (!Character .isJavaIdentifierStart (text .charAt (0 ))) return Stream .empty ();
359+ int cursor = 1 ;
360+ for (; cursor < text .length () && Character .isJavaIdentifierPart (text .charAt (cursor )); cursor ++);
361+ return IntStream .iterate (cursor , i -> i > 0 , i -> i - 1 ).boxed ()
362+ .flatMap (limit -> identifier .children ().stream ().flatMap (child -> matches (start + limit , text .substring (limit ), child ))
363+ .map (v -> v .prepend (identifier , start , text .substring (0 , limit ))));
364+ } else if (root instanceof final Branch branch ) {
365+ return branch .children ().stream ().flatMap (child -> matches (start , text , child ).map (v -> v .tag (branch , start , start + text .length ())));
366+ } else if (root instanceof final Input input ) {
367+ return IntStream .iterate (text .length (), i -> i > 0 , i -> i - 1 ).boxed ()
368+ .flatMap (limit -> input .children ().stream ().flatMap (child -> matches (start + limit , text .substring (limit ), child ))
369+ .map (v -> v .prepend (input , start , text .substring (0 , limit ))));
370+ } else if (root instanceof final Dynamic dynamic ) {
371+ final Node invoke = dynamic .supplier ().get ();
372+ return matches (start , text , invoke ).map (v -> v .tag (dynamic , start , v .endIndexOf (invoke )));
373+ } else if (root instanceof final ChildNode labelledNode ) {
374+ return matches (start , text , labelledNode .node ()).map (v -> v .tag (labelledNode , start , v .startIndexOf (labelledNode .then ())));
375+ } else if (root instanceof final Terminal terminal ) {
376+ if (terminal .context () != null && terminal .context ().redirect () != null )
377+ return matches (start , text , terminal .context ().redirect ()).map (v -> v .tag (terminal , start , start ));
378+ if (!text .isEmpty ()) return Stream .empty ();
379+ return Stream .of (new Resolved (List .of (), "" ).tag (terminal , start , start ));
380+ }
381+ throw new AssertionError ("unreachable" );
382+ }
383+ }
0 commit comments