55import gg .moonflower .molangcompiler .api .MolangExpression ;
66import gg .moonflower .molangcompiler .api .exception .MolangException ;
77
8+ import java .util .ArrayList ;
9+ import java .util .List ;
810import java .util .Locale ;
911import java .util .Map ;
1012import java .util .concurrent .ConcurrentHashMap ;
@@ -18,6 +20,12 @@ final class MolangCompilerFacade {
1820 "(?i)\\ bysm\\ .second_order\\ (\\ s*'([^']*)'\\ s*," );
1921 private static final Pattern BONE_ROTATION_CALL = Pattern .compile (
2022 "(?i)\\ bysm\\ .bone_rot\\ (\\ s*'([^']*)'\\ s*\\ )\\ s*\\ .\\ s*([xyz])\\ b" );
23+ private static final Pattern LEADING_ASSIGNMENT = Pattern .compile (
24+ "(?i)^\\ s*(?:v|variable|temp)\\ .[a-z_][a-z0-9_]*\\ s*=\\ s*(?!=)" );
25+ private static final Pattern LOGICAL_NOT_IDENTIFIER = Pattern .compile (
26+ "(?i)!(?!=)\\ s*((?:[a-z_][a-z0-9_]*\\ .)*[a-z_][a-z0-9_]*)" );
27+ private static final Pattern TRAILING_DECIMAL = Pattern .compile (
28+ "(?<![a-z0-9_.])(\\ d+)\\ .(?!\\ d)" , Pattern .CASE_INSENSITIVE );
2129
2230 private final MolangCompiler compiler = GlobalMolangCompiler .get ();
2331 private final Map <String , MolangExpression > cache = new ConcurrentHashMap <>();
@@ -31,7 +39,7 @@ final class MolangCompilerFacade {
3139 }
3240
3341 MolangExpression compile (String source ) {
34- String normalized = rewriteDynamicFunctionArguments (normalize (source ));
42+ String normalized = rewriteLegacySyntax ( rewriteDynamicFunctionArguments (normalize (source ) ));
3543 if (normalized .isBlank ()) {
3644 throw new IllegalArgumentException ("Molang expression cannot be blank" );
3745 }
@@ -98,6 +106,127 @@ static String normalize(String source) {
98106 return output .toString ();
99107 }
100108
109+ private static String rewriteLegacySyntax (String source ) {
110+ String rewritten = LEADING_ASSIGNMENT .matcher (source ).replaceFirst ("" );
111+ rewritten = TRAILING_DECIMAL .matcher (rewritten ).replaceAll ("$1.0" );
112+ rewritten = rewriteLogicalNot (rewritten );
113+ rewritten = rewriteUnaryParenthesisNegation (rewritten );
114+ return addMissingTernaryFalseBranches (rewritten );
115+ }
116+
117+ private static String rewriteLogicalNot (String source ) {
118+ Matcher identifier = LOGICAL_NOT_IDENTIFIER .matcher (source );
119+ StringBuffer output = new StringBuffer ();
120+ while (identifier .find ()) {
121+ identifier .appendReplacement (output ,
122+ Matcher .quoteReplacement ("(" + identifier .group (1 ) + "==0)" ));
123+ }
124+ identifier .appendTail (output );
125+
126+ String rewritten = output .toString ();
127+ for (int index = 0 ; index < rewritten .length (); index ++) {
128+ if (rewritten .charAt (index ) != '!' || index + 1 >= rewritten .length ()
129+ || rewritten .charAt (index + 1 ) == '=' ) continue ;
130+ int open = nextNonWhitespace (rewritten , index + 1 );
131+ if (open < 0 || rewritten .charAt (open ) != '(' ) continue ;
132+ int close = matchingParenthesis (rewritten , open );
133+ if (close < 0 ) continue ;
134+ String operand = rewritten .substring (open , close + 1 );
135+ rewritten = rewritten .substring (0 , index ) + "(" + operand + "==0)"
136+ + rewritten .substring (close + 1 );
137+ }
138+ return rewritten ;
139+ }
140+
141+ private static String rewriteUnaryParenthesisNegation (String source ) {
142+ StringBuilder output = new StringBuilder (source .length () + 8 );
143+ for (int index = 0 ; index < source .length (); index ++) {
144+ char character = source .charAt (index );
145+ if (character == '-' ) {
146+ int next = nextNonWhitespace (source , index + 1 );
147+ int previous = previousNonWhitespace (source , index - 1 );
148+ boolean unary = previous < 0 || "([{:?,=+-*/%<>!&|;" .indexOf (source .charAt (previous )) >= 0 ;
149+ if (unary && next >= 0 && source .charAt (next ) == '(' ) {
150+ output .append ("-1*" );
151+ continue ;
152+ }
153+ }
154+ output .append (character );
155+ }
156+ return output .toString ();
157+ }
158+
159+ private static String addMissingTernaryFalseBranches (String source ) {
160+ List <Integer > questions = new ArrayList <>();
161+ for (int index = 0 ; index < source .length (); index ++) {
162+ if (source .charAt (index ) == '?' ) questions .add (index );
163+ }
164+ if (questions .isEmpty ()) return source ;
165+
166+ StringBuilder rewritten = new StringBuilder (source );
167+ for (int questionIndex = questions .size () - 1 ; questionIndex >= 0 ; questionIndex --) {
168+ int question = questions .get (questionIndex );
169+ int nestedTernaries = 0 ;
170+ int depth = 0 ;
171+ int boundary = rewritten .length ();
172+ boolean hasFalseBranch = false ;
173+ for (int index = question + 1 ; index < rewritten .length (); index ++) {
174+ char character = rewritten .charAt (index );
175+ if (character == '(' || character == '[' || character == '{' ) {
176+ depth ++;
177+ continue ;
178+ }
179+ if (character == ')' || character == ']' || character == '}' ) {
180+ if (depth == 0 ) {
181+ boundary = index ;
182+ break ;
183+ }
184+ depth --;
185+ continue ;
186+ }
187+ if (depth != 0 ) continue ;
188+ if (character == '?' ) {
189+ nestedTernaries ++;
190+ } else if (character == ':' ) {
191+ if (nestedTernaries == 0 ) {
192+ hasFalseBranch = true ;
193+ break ;
194+ }
195+ nestedTernaries --;
196+ } else if ((character == ',' || character == ';' ) && nestedTernaries == 0 ) {
197+ boundary = index ;
198+ break ;
199+ }
200+ }
201+ if (!hasFalseBranch ) rewritten .insert (boundary , ":0" );
202+ }
203+ return rewritten .toString ();
204+ }
205+
206+ private static int nextNonWhitespace (String value , int start ) {
207+ for (int index = Math .max (0 , start ); index < value .length (); index ++) {
208+ if (!Character .isWhitespace (value .charAt (index ))) return index ;
209+ }
210+ return -1 ;
211+ }
212+
213+ private static int previousNonWhitespace (String value , int start ) {
214+ for (int index = Math .min (start , value .length () - 1 ); index >= 0 ; index --) {
215+ if (!Character .isWhitespace (value .charAt (index ))) return index ;
216+ }
217+ return -1 ;
218+ }
219+
220+ private static int matchingParenthesis (String value , int open ) {
221+ int depth = 0 ;
222+ for (int index = open ; index < value .length (); index ++) {
223+ char character = value .charAt (index );
224+ if (character == '(' ) depth ++;
225+ else if (character == ')' && --depth == 0 ) return index ;
226+ }
227+ return -1 ;
228+ }
229+
101230 private String rewriteDynamicFunctionArguments (String source ) {
102231 Matcher secondOrder = SECOND_ORDER_CALL .matcher (source );
103232 StringBuffer output = new StringBuffer ();
0 commit comments