1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.incubator.code.parser;
 27 
 28 import java.io.IOException;
 29 import java.io.InputStream;
 30 import jdk.incubator.code.*;
 31 import jdk.incubator.code.TypeElement.ExternalizedTypeElement;
 32 import jdk.incubator.code.parser.impl.Tokens.TokenKind;
 33 import jdk.incubator.code.type.FunctionType;
 34 import jdk.incubator.code.op.*;
 35 import jdk.incubator.code.parser.impl.DescParser;
 36 import jdk.incubator.code.parser.impl.Lexer;
 37 import jdk.incubator.code.parser.impl.Scanner;
 38 import jdk.incubator.code.parser.impl.Tokens;
 39 import jdk.incubator.code.type.CoreTypeFactory;
 40 import jdk.incubator.code.type.JavaType;
 41 import jdk.incubator.code.type.TypeElementFactory;
 42 import jdk.incubator.code.type.impl.JavaTypeUtils;
 43 
 44 import java.nio.charset.StandardCharsets;
 45 import java.util.ArrayList;
 46 import java.util.HashMap;
 47 import java.util.List;
 48 import java.util.Map;
 49 
 50 /**
 51  * A parser of serialized code models from their textual form.
 52  * <p>
 53  * The syntactic grammar of a code mode is specified in the grammar notation, and is a subset of the grammar,
 54  * specified by the JLS, see section 2.4. (Except that we cannot express non-terminal symbols in italic type.)
 55  * <p>
 56  * {@snippet lang=text :
 57  * Operation:
 58  *   [Value =] Name {Operands} {Successors} {Attributes} {Bodies} ;
 59  *
 60  * Operands:
 61  *   ValueIdentifier {ValueIdentifier}
 62  *
 63  * Successors:
 64  *   Successor {Successor}
 65  *
 66  * Successor:
 67  *   BlockIdentifier
 68  *   BlockIdentifier ()
 69  *   BlockIdentifier ( ValueIdentifier {, ValueIdentifier} )
 70  *
 71  * Attributes:
 72  *   Attribute {Attribute}
 73  *
 74  * Attribute:
 75  *   @ AttributeValue
 76  *   @ Name = AttributeValue
 77  *
 78  * AttributeValue:
 79  *   ExType
 80  *   NumericAttributeValue
 81  *   BooleanLiteral
 82  *   CharacterLiteral
 83  *   StringLiteral
 84  *   NullLiteral
 85  *
 86  * NumericAttributeValue:
 87  *    [ '-' ] IntLiteral
 88  *    [ '-' ] LongLiteral
 89  *    [ '-' ] FloatLiteral
 90  *    [ '-' ] DoubleLiteral
 91  *
 92  * Bodies:
 93  *   Body {Body}
 94  *
 95  * Body:
 96  *   BlockIdentifier ( ) Type -> { Operations {Block} }
 97  *   BlockIdentifier ( Value {, Value} ) Type -> { Operations {Block} }
 98  *
 99  * Operations:
100  *   Operation {Operation}
101  *
102  * Block:
103  *   BlockIdentifier : Operations
104  *   BlockIdentifier ( ) : Operations
105  *   BlockIdentifier ( Value {, Value} ) : Operations
106  *
107  * BlockIdentifier:
108  *   ^ Identifier
109  *
110  * Value:
111  *   ValueIdentifier : Type
112  *
113  * ValueIdentifier:
114  *   % JavaLetterOrDigit {JavaLetterOrDigit}
115  *
116  * Name:
117  *   Identifier
118  *   Name . Identifier
119  *
120  * Type:
121  *   same as in section 4.1 of JLS but without any annotations
122  *
123  * Identifier:
124  *   same as in section 3 of JLS
125  *
126  * JavaLetterOrDigit:
127  *   same as in section 3 of JLS
128  *
129  * StringLiteral:
130  *   same as in section 3 of JLS
131  *
132  * NullLiteral:
133  *   same as in section 3 of JLS
134  * }
135  */
136 public final class OpParser {
137 
138     static final TypeElement.ExternalizedTypeElement VOID = JavaType.VOID.externalize();
139 
140     /**
141      * Parse a code model from its serialized textual form obtained from an input stream.
142      *
143      * @param opFactory the operation factory used to construct operations from their general definition
144      * @param in the input stream
145      * @return the list of operations
146      * @throws IOException if parsing fails
147      */
148     public static List<Op> fromStream(OpFactory opFactory, InputStream in) throws IOException {
149         return fromStream(opFactory, CoreTypeFactory.CORE_TYPE_FACTORY, in);
150     }
151 
152     public static List<Op> fromStream(OpFactory opFactory, TypeElementFactory typeFactory, InputStream in) throws IOException {
153         String s = new String(in.readAllBytes(), StandardCharsets.UTF_8);
154         return fromString(opFactory, typeFactory, s);
155     }
156 
157     /**
158      * Parse a code model from its serialized textual form obtained from an input string.
159      *
160      * @param opFactory the operation factory used to construct operations from their general definition
161      * @param in the input string
162      * @return the list of operations
163      */
164     public static List<Op> fromString(OpFactory opFactory, String in) {
165         return parse(opFactory, CoreTypeFactory.CORE_TYPE_FACTORY, in);
166     }
167 
168     public static List<Op> fromString(OpFactory opFactory, TypeElementFactory typeFactory, String in) {
169         return parse(opFactory, typeFactory, in);
170     }
171 
172     /**
173      * Parse a code model, modeling a method, from its serialized textual form obtained from an input string.
174      *
175      * @param in the input string
176      * @return the func operation
177      */
178     //@@@ visit return type
179     public static Op fromStringOfFuncOp(String in) {
180         Op op = fromString(ExtendedOp.FACTORY, in).get(0);
181         if (!(op instanceof CoreOp.FuncOp)) {
182             throw new IllegalArgumentException("Op is not a FuncOp: " + op);
183         }
184         return op;
185     }
186 
187     static List<Op> parse(OpFactory opFactory, TypeElementFactory typeFactory, String in) {
188         Lexer lexer = Scanner.factory().newScanner(in);
189         lexer.nextToken();
190 
191         List<OpNode> opNodes = new OpParser(lexer).parseNodes();
192 
193         Context c = new Context(opFactory, typeFactory);
194         return opNodes.stream().map(n -> nodeToOp(n, VOID, c, null)).toList();
195     }
196 
197 
198     static final class Context {
199         final Context parent;
200         final OpFactory opFactory;
201         final TypeElementFactory typeFactory;
202         final Map<String, Value> valueMap;
203         final Map<String, Block.Builder> blockMap;
204 
205         Context(Context that, boolean isolated) {
206             this.parent = that;
207             this.opFactory = that.opFactory;
208             this.typeFactory = that.typeFactory;
209             this.valueMap = isolated ? new HashMap<>() : new HashMap<>(that.valueMap);
210             this.blockMap = new HashMap<>();
211         }
212 
213         Context(OpFactory opFactory, TypeElementFactory typeFactory) {
214             this.parent = null;
215             this.opFactory = opFactory;
216             this.typeFactory = typeFactory;
217             this.valueMap = new HashMap<>();
218             this.blockMap = new HashMap<>();
219         }
220 
221         Context fork(boolean isolated) {
222             return new Context(this, isolated);
223         }
224 
225         void putValue(String name, Value opr) {
226             valueMap.put(name, opr);
227         }
228 
229         Value getValue(String name) {
230             Value value = valueMap.get(name);
231             if (value == null) {
232                 // @@@ location
233                 throw new IllegalArgumentException("Undeclared value referenced: " + name);
234             }
235 
236             return value;
237         }
238 
239         void putBlock(String name, Block.Builder bm) {
240             blockMap.put(name, bm);
241         }
242 
243         Block.Builder getBlock(String name) {
244             Block.Builder block = blockMap.get(name);
245             if (block == null) {
246                 // @@@ location
247                 throw new IllegalArgumentException("Undeclared block referenced: " + name);
248             }
249 
250             return block;
251         }
252     }
253 
254     static Op nodeToOp(OpNode opNode, TypeElement.ExternalizedTypeElement rtype, Context c, Body.Builder ancestorBody) {
255         ExternalizableOp.ExternalizedOp opdef = nodeToOpDef(opNode, rtype, c, ancestorBody);
256         return c.opFactory.constructOpOrFail(opdef);
257     }
258 
259     static ExternalizableOp.ExternalizedOp nodeToOpDef(OpNode opNode, TypeElement.ExternalizedTypeElement rtype, Context c, Body.Builder ancestorBody) {
260         String operationName = opNode.name;
261         List<Value> operands = opNode.operands.stream().map(c::getValue).toList();
262         List<Block.Reference> successors = opNode.successors.stream()
263                 .map(n -> nodeToSuccessor(n, c)).toList();
264         List<Body.Builder> bodies = opNode.bodies.stream()
265                 .map(n -> nodeToBody(n, c.fork(false), ancestorBody)).toList();
266         return new ExternalizableOp.ExternalizedOp(operationName,
267                 operands,
268                 successors,
269                 c.typeFactory.constructType(rtype),
270                 inflateAttributes(opNode.attributes, c.typeFactory),
271                 bodies);
272     }
273 
274     static Map<String, Object> inflateAttributes(Map<String, Object> attributes, TypeElementFactory typeFactory) {
275         Map<String, Object> newAttributes = new HashMap<>();
276         for (Map.Entry<String, Object> e : attributes.entrySet()) {
277             String name = e.getKey();
278             Object value = e.getValue();
279             if (value instanceof ExternalizedTypeElement ete) {
280                 value = typeFactory.constructType(JavaTypeUtils.inflate(ete));
281             }
282             newAttributes.put(name, value);
283         }
284         return newAttributes;
285     }
286 
287     static Body.Builder nodeToBody(BodyNode n, Context c, Body.Builder ancestorBody) {
288         Body.Builder body = Body.Builder.of(ancestorBody,
289                 // Create function type with just the return type and add parameters afterward
290                 FunctionType.functionType(c.typeFactory.constructType(n.rtype)));
291         Block.Builder eb = body.entryBlock();
292 
293         // Create blocks upfront for forward referencing successors
294         for (int i = 0; i < n.blocks.size(); i++) {
295             BlockNode bn = n.blocks.get(i);
296             Block.Builder b;
297             if (i == 0) {
298                 b = body.entryBlock();
299             } else {
300                 b = eb.block();
301                 c.putBlock(bn.name, b);
302             }
303 
304             for (ValueNode a : bn.parameters) {
305                 Block.Parameter v = b.parameter(c.typeFactory.constructType(a.type));
306                 c.putValue(a.name, v);
307             }
308         }
309 
310         // Create operations
311         for (int i = 0; i < n.blocks.size(); i++) {
312             BlockNode bn = n.blocks.get(i);
313             Block.Builder b;
314             if (i == 0) {
315                 b = body.entryBlock();
316             } else {
317                 b = c.getBlock(n.blocks.get(i).name);
318             }
319 
320             for (OpNode on : bn.ops) {
321                 ValueNode r = on.result;
322                 if (r != null) {
323                     Op.Result v = b.op(nodeToOp(on, r.type, c, body));
324                     c.putValue(r.name, v);
325                 } else {
326                     b.op(nodeToOp(on, VOID, c, body));
327                 }
328             }
329         }
330 
331         return body;
332     }
333 
334     static Block.Reference nodeToSuccessor(SuccessorNode n, Context c) {
335         return c.getBlock(n.blockName).successor(n.arguments().stream().map(c::getValue).toList());
336     }
337 
338     // @@@ Add tokens to access position of nodes on error
339 
340     record OpNode(ValueNode result,
341                   String name,
342                   List<String> operands,
343                   List<SuccessorNode> successors,
344                   Map<String, Object> attributes,
345                   List<BodyNode> bodies) {
346     }
347 
348     record SuccessorNode(String blockName,
349                          List<String> arguments) {
350     }
351 
352     record BodyNode(TypeElement.ExternalizedTypeElement rtype,
353                     List<BlockNode> blocks) {
354     }
355 
356     record BlockNode(String name,
357                      List<ValueNode> parameters,
358                      List<OpNode> ops) {
359     }
360 
361     record ValueNode(String name,
362                      TypeElement.ExternalizedTypeElement type) {
363     }
364 
365     final Lexer lexer;
366 
367     OpParser(Lexer lexer) {
368         this.lexer = lexer;
369     }
370 
371     List<OpNode> parseNodes() {
372         List<OpNode> ops = new ArrayList<>();
373         while (lexer.token().kind != Tokens.TokenKind.EOF) {
374             ops.add(parseOperation());
375         }
376         return ops;
377     }
378 
379     OpNode parseOperation() {
380         ValueNode operationResult;
381         if (lexer.is(Tokens.TokenKind.VALUE_IDENTIFIER)) {
382             operationResult = parseValueNode();
383             lexer.accept(Tokens.TokenKind.EQ);
384         } else {
385             operationResult = null;
386         }
387 
388         String operationName = parseName();
389 
390         // Operands
391         final List<String> operands;
392         if (lexer.is(Tokens.TokenKind.VALUE_IDENTIFIER)) {
393             operands = parseOperands();
394         } else {
395             operands = List.of();
396         }
397 
398         // Successors
399         // ^name(%x, %d)
400         final List<SuccessorNode> successors;
401         if (lexer.is(Tokens.TokenKind.CARET)) {
402             successors = parseSuccessors();
403         } else {
404             successors = List.of();
405         }
406 
407         // Attributes
408         final Map<String, Object> attributes;
409         if (lexer.is(Tokens.TokenKind.MONKEYS_AT)) {
410             attributes = parseAttributes();
411         } else {
412             attributes = Map.of();
413         }
414 
415         // Bodies
416         List<BodyNode> bodies;
417         if (lexer.is(Tokens.TokenKind.CARET) || lexer.is(Tokens.TokenKind.LPAREN)) {
418             bodies = parseBodies();
419         } else {
420             bodies = List.of();
421         }
422 
423         lexer.accept(Tokens.TokenKind.SEMI);
424 
425         return new OpNode(operationResult, operationName, operands, successors, attributes, bodies);
426     }
427 
428     Map<String, Object> parseAttributes() {
429         Map<String, Object> attributes = new HashMap<>();
430         while (lexer.acceptIf(Tokens.TokenKind.MONKEYS_AT)) {
431             String attributeName;
432             if (isNameStart()) {
433                 attributeName = parseName();
434                 lexer.accept(Tokens.TokenKind.EQ);
435             } else {
436                 attributeName = "";
437             }
438             Object attributeValue = parseAttributeValue();
439             attributes.put(attributeName, attributeValue);
440         }
441         return attributes;
442     }
443 
444     boolean isNameStart() {
445         // we need to lookahead to see if we can find an identifier followed by a '=',
446         // in which case we know what we're looking at is an attribute name
447         int curr = 0;
448         while (true) {
449             if (lexer.token(curr++).kind != TokenKind.IDENTIFIER) {
450                 return false;
451             }
452             TokenKind next = lexer.token(curr++).kind;
453             if (next == TokenKind.EQ) {
454                 return true;
455             } else if (next != TokenKind.DOT) {
456                 return false;
457             }
458         }
459     }
460 
461     Object parseAttributeValue() {
462         if (lexer.is(Tokens.TokenKind.IDENTIFIER)) {
463             return DescParser.parseExTypeElem(lexer);
464         }
465 
466         Object value = parseLiteral(lexer.token());
467         lexer.nextToken();
468 
469         return value;
470     }
471 
472     Object parseLiteral(Tokens.Token t) {
473         return switch (t.kind) {
474             case STRINGLITERAL -> t.stringVal();
475             case NULL -> ExternalizableOp.NULL_ATTRIBUTE_VALUE;
476             case CHARLITERAL -> t.stringVal().charAt(0);
477             case TRUE -> true;
478             case FALSE -> false;
479             default -> parseNumericLiteral(t);
480         };
481     }
482 
483     Object parseNumericLiteral(Tokens.Token t) {
484         String minusOpt = "";
485         if (t.kind == TokenKind.SUB) {
486             minusOpt = "-";
487             lexer.nextToken();
488             t = lexer.token();
489         }
490         return switch (t.kind) {
491             case INTLITERAL -> Integer.valueOf(minusOpt + t.stringVal());
492             case LONGLITERAL -> Long.valueOf(minusOpt + t.stringVal());
493             case FLOATLITERAL -> Float.valueOf(minusOpt + t.stringVal());
494             case DOUBLELITERAL -> Double.valueOf(minusOpt + t.stringVal());
495             default -> throw lexer.unexpected();
496         };
497     }
498 
499     List<String> parseOperands() {
500         List<String> operands = new ArrayList<>();
501         while (lexer.is(Tokens.TokenKind.VALUE_IDENTIFIER)) {
502             operands.add(lexer.token().name().substring(1));
503             lexer.nextToken();
504         }
505         return operands;
506     }
507 
508     List<SuccessorNode> parseSuccessors() {
509         List<SuccessorNode> successors = new ArrayList<>();
510 
511         while (lexer.is(Tokens.TokenKind.CARET) && !isBody()) {
512             lexer.nextToken();
513             successors.add(parseSuccessor());
514         }
515 
516         return successors;
517     }
518 
519     // Lookahead from "^" to determine if Body
520     boolean isBody() {
521         assert lexer.is(Tokens.TokenKind.CARET);
522 
523         int pos = 1;
524         lexer.token(pos++);
525         assert lexer.token(1).kind == Tokens.TokenKind.IDENTIFIER;
526 
527         if (lexer.token(pos++).kind != Tokens.TokenKind.LPAREN) {
528             return false;
529         }
530 
531         Tokens.Token t;
532         while ((t = lexer.token(pos++)).kind != Tokens.TokenKind.RPAREN) {
533             if (t.kind == Tokens.TokenKind.EOF) {
534                 return false;
535             } else if (t.kind == Tokens.TokenKind.COLON) {
536                 // Encountered Value
537                 return true;
538             }
539         }
540 
541         // Encountered return type
542         return lexer.token(pos++).kind == Tokens.TokenKind.IDENTIFIER;
543     }
544 
545     SuccessorNode parseSuccessor() {
546         String blockName = lexer.accept(Tokens.TokenKind.IDENTIFIER).name();
547 
548         List<String> arguments = new ArrayList<>();
549         if (lexer.acceptIf(Tokens.TokenKind.LPAREN) && !lexer.acceptIf(Tokens.TokenKind.RPAREN)) {
550             do {
551                 arguments.add(lexer.accept(Tokens.TokenKind.VALUE_IDENTIFIER).name().substring(1));
552             } while (lexer.acceptIf(Tokens.TokenKind.COMMA));
553             lexer.accept(Tokens.TokenKind.RPAREN);
554         }
555 
556         return new SuccessorNode(blockName, arguments);
557     }
558 
559     List<BodyNode> parseBodies() {
560         List<BodyNode> bodies = new ArrayList<>();
561         while (lexer.is(Tokens.TokenKind.CARET) || lexer.is(Tokens.TokenKind.LPAREN)) {
562             BodyNode body = parseBody();
563             bodies.add(body);
564         }
565         return bodies;
566     }
567 
568     BodyNode parseBody() {
569         // Body name
570         final String bodyName;
571         if (lexer.acceptIf(Tokens.TokenKind.CARET)) {
572             bodyName = lexer.accept(Tokens.TokenKind.IDENTIFIER).name();
573         } else {
574             bodyName = null;
575         }
576 
577         // Entry block header
578         List<ValueNode> arguments = parseBlockHeaderArguments(true);
579         // Body return type
580         TypeElement.ExternalizedTypeElement rtype = parseExTypeElem();
581 
582         lexer.accept(Tokens.TokenKind.ARROW);
583         lexer.accept(Tokens.TokenKind.LBRACE);
584 
585         List<BlockNode> blocks = parseBlocks(bodyName, arguments);
586 
587         lexer.accept(Tokens.TokenKind.RBRACE);
588 
589         return new BodyNode(rtype, blocks);
590     }
591 
592     List<ValueNode> parseBlockHeaderArguments(boolean isEntryBlock) {
593         boolean parseArguments;
594         if (isEntryBlock) {
595             lexer.accept(Tokens.TokenKind.LPAREN);
596             parseArguments = true;
597         } else {
598             parseArguments = lexer.acceptIf(Tokens.TokenKind.LPAREN);
599         }
600         if (!parseArguments || lexer.acceptIf(Tokens.TokenKind.RPAREN)) {
601             return new ArrayList<>();
602         }
603 
604         List<ValueNode> arguments = new ArrayList<>();
605         do {
606             arguments.add(parseValueNode());
607         } while (lexer.acceptIf(Tokens.TokenKind.COMMA));
608         lexer.accept(Tokens.TokenKind.RPAREN);
609 
610         return arguments;
611     }
612 
613     ValueNode parseValueNode() {
614         String valueName = lexer.accept(Tokens.TokenKind.VALUE_IDENTIFIER).name().substring(1);
615 
616         lexer.accept(Tokens.TokenKind.COLON);
617 
618         TypeElement.ExternalizedTypeElement type = parseExTypeElem();
619 
620         return new ValueNode(valueName, type);
621     }
622 
623     List<BlockNode> parseBlocks(String entryBlockName, List<ValueNode> entryBlockArguments) {
624         List<BlockNode> blocks = new ArrayList<>();
625 
626         // Entry block ops
627         BlockNode entryBlock = new BlockNode(entryBlockName, entryBlockArguments, parseOperations());
628         blocks.add(entryBlock);
629 
630         // Subsequent blocks
631         while (lexer.acceptIf(Tokens.TokenKind.CARET)) {
632             String blockName = lexer.accept(Tokens.TokenKind.IDENTIFIER).name();
633             List<ValueNode> blockArguments = parseBlockHeaderArguments(false);
634 
635             lexer.accept(Tokens.TokenKind.COLON);
636 
637             BlockNode block = new BlockNode(blockName, blockArguments, parseOperations());
638             blocks.add(block);
639         }
640 
641         return blocks;
642     }
643 
644     List<OpNode> parseOperations() {
645         List<OpNode> ops = new ArrayList<>();
646         while (lexer.is(Tokens.TokenKind.MONKEYS_AT) || lexer.is(Tokens.TokenKind.VALUE_IDENTIFIER) || lexer.is(Tokens.TokenKind.IDENTIFIER)) {
647             OpNode op = parseOperation();
648             ops.add(op);
649         }
650         return ops;
651     }
652 
653     String parseName() {
654         Tokens.Token t = lexer.accept(Tokens.TokenKind.IDENTIFIER);
655         StringBuilder name = new StringBuilder();
656         name.append(t.name());
657         while (lexer.acceptIf(Tokens.TokenKind.DOT)) {
658             name.append(Tokens.TokenKind.DOT.name);
659             t = lexer.accept(Tokens.TokenKind.IDENTIFIER);
660             name.append(t.name());
661         }
662         return name.toString();
663     }
664 
665     TypeElement.ExternalizedTypeElement parseExTypeElem() {
666         return JavaTypeUtils.inflate(DescParser.parseExTypeElem(lexer));
667     }
668 }
669