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.type.FunctionType;
 32 import jdk.incubator.code.op.*;
 33 import jdk.incubator.code.parser.impl.DescParser;
 34 import jdk.incubator.code.parser.impl.Lexer;
 35 import jdk.incubator.code.parser.impl.Scanner;
 36 import jdk.incubator.code.parser.impl.Tokens;
 37 import jdk.incubator.code.type.CoreTypeFactory;
 38 import jdk.incubator.code.type.TypeElementFactory;
 39 import java.nio.charset.StandardCharsets;
 40 import java.util.ArrayList;
 41 import java.util.HashMap;
 42 import java.util.List;
 43 import java.util.Map;
 44 
 45 /**
 46  * A parser of serialized code models from their textual form.
 47  * <p>
 48  * The syntactic grammar of a code mode is specified in the grammar notation, and is a subset of the grammar,
 49  * specified by the JLS, see section 2.4. (Except that we cannot express non-terminal symbols in italic type.)
 50  * <p>
 51  * {@snippet lang=text :
 52  * Operation:
 53  *   [Value =] Name {Operands} {Successors} {Attributes} {Bodies} ;
 54  *
 55  * Operands:
 56  *   ValueIdentifier {ValueIdentifier}
 57  *
 58  * Successors:
 59  *   Successor {Successor}
 60  *
 61  * Successor:
 62  *   BlockIdentifier
 63  *   BlockIdentifier ()
 64  *   BlockIdentifier ( ValueIdentifier {, ValueIdentifier} )
 65  *
 66  * Attributes:
 67  *   Attribute {Attribute}
 68  *
 69  * Attribute:
 70  *   @ AttributeValue
 71  *   @ Name = AttributeValue
 72  *
 73  * AttributeValue:
 74  *   Name
 75  *   StringLiteral
 76  *   NullLiteral
 77  *
 78  * Bodies:
 79  *   Body {Body}
 80  *
 81  * Body:
 82  *   BlockIdentifier ( ) Type -> { Operations {Block} }
 83  *   BlockIdentifier ( Value {, Value} ) Type -> { Operations {Block} }
 84  *
 85  * Operations:
 86  *   Operation {Operation}
 87  *
 88  * Block:
 89  *   BlockIdentifier : Operations
 90  *   BlockIdentifier ( ) : Operations
 91  *   BlockIdentifier ( Value {, Value} ) : Operations
 92  *
 93  * BlockIdentifier:
 94  *   ^ Identifier
 95  *
 96  * Value:
 97  *   ValueIdentifier : Type
 98  *
 99  * ValueIdentifier:
100  *   % JavaLetterOrDigit {JavaLetterOrDigit}
101  *
102  * Name:
103  *   Identifier
104  *   Name . Identifier
105  *
106  * Type:
107  *   same as in section 4.1 of JLS but without any annotations
108  *
109  * Identifier:
110  *   same as in section 3 of JLS
111  *
112  * JavaLetterOrDigit:
113  *   same as in section 3 of JLS
114  *
115  * StringLiteral:
116  *   same as in section 3 of JLS
117  *
118  * NullLiteral:
119  *   same as in section 3 of JLS
120  * }
121  */
122 public final class OpParser {
123 
124     static final TypeElement.ExternalizedTypeElement VOID =
125             new TypeElement.ExternalizedTypeElement("void", List.of());
126 
127     /**
128      * Parse a code model from its serialized textual form obtained from an input stream.
129      *
130      * @param opFactory the operation factory used to construct operations from their general definition
131      * @param in the input stream
132      * @return the list of operations
133      * @throws IOException if parsing fails
134      */
135     public static List<Op> fromStream(OpFactory opFactory, InputStream in) throws IOException {
136         return fromStream(opFactory, CoreTypeFactory.CORE_TYPE_FACTORY, in);
137     }
138 
139     public static List<Op> fromStream(OpFactory opFactory, TypeElementFactory typeFactory, InputStream in) throws IOException {
140         String s = new String(in.readAllBytes(), StandardCharsets.UTF_8);
141         return fromString(opFactory, typeFactory, s);
142     }
143 
144     /**
145      * Parse a code model from its serialized textual form obtained from an input string.
146      *
147      * @param opFactory the operation factory used to construct operations from their general definition
148      * @param in the input string
149      * @return the list of operations
150      */
151     public static List<Op> fromString(OpFactory opFactory, String in) {
152         return parse(opFactory, CoreTypeFactory.CORE_TYPE_FACTORY, in);
153     }
154 
155     public static List<Op> fromString(OpFactory opFactory, TypeElementFactory typeFactory, String in) {
156         return parse(opFactory, typeFactory, in);
157     }
158 
159     /**
160      * Parse a code model, modeling a method, from its serialized textual form obtained from an input string.
161      *
162      * @param in the input string
163      * @return the func operation
164      */
165     //@@@ visit return type
166     public static Op fromStringOfFuncOp(String in) {
167         Op op = fromString(ExtendedOp.FACTORY, in).get(0);
168         if (!(op instanceof CoreOp.FuncOp)) {
169             throw new IllegalArgumentException("Op is not a FuncOp: " + op);
170         }
171         return op;
172     }
173 
174     static List<Op> parse(OpFactory opFactory, TypeElementFactory typeFactory, String in) {
175         Lexer lexer = Scanner.factory().newScanner(in);
176         lexer.nextToken();
177 
178         List<OpNode> opNodes = new OpParser(lexer).parseNodes();
179 
180         Context c = new Context(opFactory, typeFactory);
181         return opNodes.stream().map(n -> nodeToOp(n, VOID, c, null)).toList();
182     }
183 
184 
185     static final class Context {
186         final Context parent;
187         final OpFactory opFactory;
188         final TypeElementFactory typeFactory;
189         final Map<String, Value> valueMap;
190         final Map<String, Block.Builder> blockMap;
191 
192         Context(Context that, boolean isolated) {
193             this.parent = that;
194             this.opFactory = that.opFactory;
195             this.typeFactory = that.typeFactory;
196             this.valueMap = isolated ? new HashMap<>() : new HashMap<>(that.valueMap);
197             this.blockMap = new HashMap<>();
198         }
199 
200         Context(OpFactory opFactory, TypeElementFactory typeFactory) {
201             this.parent = null;
202             this.opFactory = opFactory;
203             this.typeFactory = typeFactory;
204             this.valueMap = new HashMap<>();
205             this.blockMap = new HashMap<>();
206         }
207 
208         Context fork(boolean isolated) {
209             return new Context(this, isolated);
210         }
211 
212         void putValue(String name, Value opr) {
213             valueMap.put(name, opr);
214         }
215 
216         Value getValue(String name) {
217             Value value = valueMap.get(name);
218             if (value == null) {
219                 // @@@ location
220                 throw new IllegalArgumentException("Undeclared value referenced: " + name);
221             }
222 
223             return value;
224         }
225 
226         void putBlock(String name, Block.Builder bm) {
227             blockMap.put(name, bm);
228         }
229 
230         Block.Builder getBlock(String name) {
231             Block.Builder block = blockMap.get(name);
232             if (block == null) {
233                 // @@@ location
234                 throw new IllegalArgumentException("Undeclared block referenced: " + name);
235             }
236 
237             return block;
238         }
239     }
240 
241     static Op nodeToOp(OpNode opNode, TypeElement.ExternalizedTypeElement rtype, Context c, Body.Builder ancestorBody) {
242         ExternalizableOp.ExternalizedOp opdef = nodeToOpDef(opNode, rtype, c, ancestorBody);
243         return c.opFactory.constructOpOrFail(opdef);
244     }
245 
246     static ExternalizableOp.ExternalizedOp nodeToOpDef(OpNode opNode, TypeElement.ExternalizedTypeElement rtype, Context c, Body.Builder ancestorBody) {
247         String operationName = opNode.name;
248         List<Value> operands = opNode.operands.stream().map(c::getValue).toList();
249         List<Block.Reference> successors = opNode.successors.stream()
250                 .map(n -> nodeToSuccessor(n, c)).toList();
251         List<Body.Builder> bodies = opNode.bodies.stream()
252                 .map(n -> nodeToBody(n, c.fork(false), ancestorBody)).toList();
253         return new ExternalizableOp.ExternalizedOp(operationName,
254                 operands,
255                 successors,
256                 c.typeFactory.constructType(rtype),
257                 opNode.attributes,
258                 bodies);
259     }
260 
261     static Body.Builder nodeToBody(BodyNode n, Context c, Body.Builder ancestorBody) {
262         Body.Builder body = Body.Builder.of(ancestorBody,
263                 // Create function type with just the return type and add parameters afterward
264                 FunctionType.functionType(c.typeFactory.constructType(n.rtype)));
265         Block.Builder eb = body.entryBlock();
266 
267         // Create blocks upfront for forward referencing successors
268         for (int i = 0; i < n.blocks.size(); i++) {
269             BlockNode bn = n.blocks.get(i);
270             Block.Builder b;
271             if (i == 0) {
272                 b = body.entryBlock();
273             } else {
274                 b = eb.block();
275                 c.putBlock(bn.name, b);
276             }
277 
278             for (ValueNode a : bn.parameters) {
279                 Block.Parameter v = b.parameter(c.typeFactory.constructType(a.type));
280                 c.putValue(a.name, v);
281             }
282         }
283 
284         // Create operations
285         for (int i = 0; i < n.blocks.size(); i++) {
286             BlockNode bn = n.blocks.get(i);
287             Block.Builder b;
288             if (i == 0) {
289                 b = body.entryBlock();
290             } else {
291                 b = c.getBlock(n.blocks.get(i).name);
292             }
293 
294             for (OpNode on : bn.ops) {
295                 ValueNode r = on.result;
296                 if (r != null) {
297                     Op.Result v = b.op(nodeToOp(on, r.type, c, body));
298                     c.putValue(r.name, v);
299                 } else {
300                     b.op(nodeToOp(on, VOID, c, body));
301                 }
302             }
303         }
304 
305         return body;
306     }
307 
308     static Block.Reference nodeToSuccessor(SuccessorNode n, Context c) {
309         return c.getBlock(n.blockName).successor(n.arguments().stream().map(c::getValue).toList());
310     }
311 
312     // @@@ Add tokens to access position of nodes on error
313 
314     record OpNode(ValueNode result,
315                   String name,
316                   List<String> operands,
317                   List<SuccessorNode> successors,
318                   Map<String, Object> attributes,
319                   List<BodyNode> bodies) {
320     }
321 
322     record SuccessorNode(String blockName,
323                          List<String> arguments) {
324     }
325 
326     record BodyNode(TypeElement.ExternalizedTypeElement rtype,
327                     List<BlockNode> blocks) {
328     }
329 
330     record BlockNode(String name,
331                      List<ValueNode> parameters,
332                      List<OpNode> ops) {
333     }
334 
335     record ValueNode(String name,
336                      TypeElement.ExternalizedTypeElement type) {
337     }
338 
339     final Lexer lexer;
340 
341     OpParser(Lexer lexer) {
342         this.lexer = lexer;
343     }
344 
345     List<OpNode> parseNodes() {
346         List<OpNode> ops = new ArrayList<>();
347         while (lexer.token().kind != Tokens.TokenKind.EOF) {
348             ops.add(parseOperation());
349         }
350         return ops;
351     }
352 
353     OpNode parseOperation() {
354         ValueNode operationResult;
355         if (lexer.is(Tokens.TokenKind.VALUE_IDENTIFIER)) {
356             operationResult = parseValueNode();
357             lexer.accept(Tokens.TokenKind.EQ);
358         } else {
359             operationResult = null;
360         }
361 
362         String operationName = parseName();
363 
364         // Operands
365         final List<String> operands;
366         if (lexer.is(Tokens.TokenKind.VALUE_IDENTIFIER)) {
367             operands = parseOperands();
368         } else {
369             operands = List.of();
370         }
371 
372         // Successors
373         // ^name(%x, %d)
374         final List<SuccessorNode> successors;
375         if (lexer.is(Tokens.TokenKind.CARET)) {
376             successors = parseSuccessors();
377         } else {
378             successors = List.of();
379         }
380 
381         // Attributes
382         final Map<String, Object> attributes;
383         if (lexer.is(Tokens.TokenKind.MONKEYS_AT)) {
384             attributes = parseAttributes();
385         } else {
386             attributes = Map.of();
387         }
388 
389         // Bodies
390         List<BodyNode> bodies;
391         if (lexer.is(Tokens.TokenKind.CARET) || lexer.is(Tokens.TokenKind.LPAREN)) {
392             bodies = parseBodies();
393         } else {
394             bodies = List.of();
395         }
396 
397         lexer.accept(Tokens.TokenKind.SEMI);
398 
399         return new OpNode(operationResult, operationName, operands, successors, attributes, bodies);
400     }
401 
402     Map<String, Object> parseAttributes() {
403         Map<String, Object> attributes = new HashMap<>();
404         while (lexer.acceptIf(Tokens.TokenKind.MONKEYS_AT)) {
405             String attributeName;
406             if (lexer.is(Tokens.TokenKind.IDENTIFIER)) {
407                 attributeName = parseName();
408                 lexer.accept(Tokens.TokenKind.EQ);
409             } else {
410                 attributeName = "";
411             }
412             Object attributeValue = parseAttributeValue();
413             attributes.put(attributeName, attributeValue);
414         }
415         return attributes;
416     }
417 
418     Object parseAttributeValue() {
419         if (lexer.is(Tokens.TokenKind.IDENTIFIER)) {
420             return parseName();
421         }
422 
423         Object value = parseLiteral(lexer.token());
424         lexer.nextToken();
425 
426         return value;
427     }
428 
429     Object parseLiteral(Tokens.Token t) {
430         return switch (t.kind) {
431             case STRINGLITERAL -> t.stringVal();
432             case NULL -> ExternalizableOp.NULL_ATTRIBUTE_VALUE;
433             default -> throw lexer.unexpected();
434         };
435     }
436 
437     List<String> parseOperands() {
438         List<String> operands = new ArrayList<>();
439         while (lexer.is(Tokens.TokenKind.VALUE_IDENTIFIER)) {
440             operands.add(lexer.token().name().substring(1));
441             lexer.nextToken();
442         }
443         return operands;
444     }
445 
446     List<SuccessorNode> parseSuccessors() {
447         List<SuccessorNode> successors = new ArrayList<>();
448 
449         while (lexer.is(Tokens.TokenKind.CARET) && !isBody()) {
450             lexer.nextToken();
451             successors.add(parseSuccessor());
452         }
453 
454         return successors;
455     }
456 
457     // Lookahead from "^" to determine if Body
458     boolean isBody() {
459         assert lexer.is(Tokens.TokenKind.CARET);
460 
461         int pos = 1;
462         lexer.token(pos++);
463         assert lexer.token(1).kind == Tokens.TokenKind.IDENTIFIER;
464 
465         if (lexer.token(pos++).kind != Tokens.TokenKind.LPAREN) {
466             return false;
467         }
468 
469         Tokens.Token t;
470         while ((t = lexer.token(pos++)).kind != Tokens.TokenKind.RPAREN) {
471             if (t.kind == Tokens.TokenKind.EOF) {
472                 return false;
473             } else if (t.kind == Tokens.TokenKind.COLON) {
474                 // Encountered Value
475                 return true;
476             }
477         }
478 
479         // Encountered return type
480         return lexer.token(pos++).kind == Tokens.TokenKind.IDENTIFIER;
481     }
482 
483     SuccessorNode parseSuccessor() {
484         String blockName = lexer.accept(Tokens.TokenKind.IDENTIFIER).name();
485 
486         List<String> arguments = new ArrayList<>();
487         if (lexer.acceptIf(Tokens.TokenKind.LPAREN) && !lexer.acceptIf(Tokens.TokenKind.RPAREN)) {
488             do {
489                 arguments.add(lexer.accept(Tokens.TokenKind.VALUE_IDENTIFIER).name().substring(1));
490             } while (lexer.acceptIf(Tokens.TokenKind.COMMA));
491             lexer.accept(Tokens.TokenKind.RPAREN);
492         }
493 
494         return new SuccessorNode(blockName, arguments);
495     }
496 
497     List<BodyNode> parseBodies() {
498         List<BodyNode> bodies = new ArrayList<>();
499         while (lexer.is(Tokens.TokenKind.CARET) || lexer.is(Tokens.TokenKind.LPAREN)) {
500             BodyNode body = parseBody();
501             bodies.add(body);
502         }
503         return bodies;
504     }
505 
506     BodyNode parseBody() {
507         // Body name
508         final String bodyName;
509         if (lexer.acceptIf(Tokens.TokenKind.CARET)) {
510             bodyName = lexer.accept(Tokens.TokenKind.IDENTIFIER).name();
511         } else {
512             bodyName = null;
513         }
514 
515         // Entry block header
516         List<ValueNode> arguments = parseBlockHeaderArguments(true);
517         // Body return type
518         TypeElement.ExternalizedTypeElement rtype = parseExTypeElem();
519 
520         lexer.accept(Tokens.TokenKind.ARROW);
521         lexer.accept(Tokens.TokenKind.LBRACE);
522 
523         List<BlockNode> blocks = parseBlocks(bodyName, arguments);
524 
525         lexer.accept(Tokens.TokenKind.RBRACE);
526 
527         return new BodyNode(rtype, blocks);
528     }
529 
530     List<ValueNode> parseBlockHeaderArguments(boolean isEntryBlock) {
531         boolean parseArguments;
532         if (isEntryBlock) {
533             lexer.accept(Tokens.TokenKind.LPAREN);
534             parseArguments = true;
535         } else {
536             parseArguments = lexer.acceptIf(Tokens.TokenKind.LPAREN);
537         }
538         if (!parseArguments || lexer.acceptIf(Tokens.TokenKind.RPAREN)) {
539             return new ArrayList<>();
540         }
541 
542         List<ValueNode> arguments = new ArrayList<>();
543         do {
544             arguments.add(parseValueNode());
545         } while (lexer.acceptIf(Tokens.TokenKind.COMMA));
546         lexer.accept(Tokens.TokenKind.RPAREN);
547 
548         return arguments;
549     }
550 
551     ValueNode parseValueNode() {
552         String valueName = lexer.accept(Tokens.TokenKind.VALUE_IDENTIFIER).name().substring(1);
553 
554         lexer.accept(Tokens.TokenKind.COLON);
555 
556         TypeElement.ExternalizedTypeElement type = parseExTypeElem();
557 
558         return new ValueNode(valueName, type);
559     }
560 
561     List<BlockNode> parseBlocks(String entryBlockName, List<ValueNode> entryBlockArguments) {
562         List<BlockNode> blocks = new ArrayList<>();
563 
564         // Entry block ops
565         BlockNode entryBlock = new BlockNode(entryBlockName, entryBlockArguments, parseOperations());
566         blocks.add(entryBlock);
567 
568         // Subsequent blocks
569         while (lexer.acceptIf(Tokens.TokenKind.CARET)) {
570             String blockName = lexer.accept(Tokens.TokenKind.IDENTIFIER).name();
571             List<ValueNode> blockArguments = parseBlockHeaderArguments(false);
572 
573             lexer.accept(Tokens.TokenKind.COLON);
574 
575             BlockNode block = new BlockNode(blockName, blockArguments, parseOperations());
576             blocks.add(block);
577         }
578 
579         return blocks;
580     }
581 
582     List<OpNode> parseOperations() {
583         List<OpNode> ops = new ArrayList<>();
584         while (lexer.is(Tokens.TokenKind.MONKEYS_AT) || lexer.is(Tokens.TokenKind.VALUE_IDENTIFIER) || lexer.is(Tokens.TokenKind.IDENTIFIER)) {
585             OpNode op = parseOperation();
586             ops.add(op);
587         }
588         return ops;
589     }
590 
591     String parseName() {
592         Tokens.Token t = lexer.accept(Tokens.TokenKind.IDENTIFIER);
593         StringBuilder name = new StringBuilder();
594         name.append(t.name());
595         while (lexer.acceptIf(Tokens.TokenKind.DOT)) {
596             name.append(Tokens.TokenKind.DOT.name);
597             t = lexer.accept(Tokens.TokenKind.IDENTIFIER);
598             name.append(t.name());
599         }
600         return name.toString();
601     }
602 
603     TypeElement.ExternalizedTypeElement parseExTypeElem() {
604         return DescParser.parseExTypeElem(lexer);
605     }
606 }
607