2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package gov.nasa.jpf.util.script;
21 import java.io.FileNotFoundException;
22 import java.io.FileReader;
23 import java.io.IOException;
24 import java.io.PrintWriter;
25 import java.io.Reader;
26 import java.io.StreamTokenizer;
27 import java.util.ArrayList;
28 import java.util.LinkedHashMap;
29 import java.util.List;
33 * generic parser for event scripts
35 * <2do> this is still awfully hardwired to StringExpander
38 public class ESParser {
40 /**** our keywords ****/
41 final public static String K_REPEAT = "REPEAT";
42 final public static String K_ANY = "ANY";
43 final public static String K_SECTION = "SECTION";
46 StreamTokenizer scanner;
50 EventFactory eventFactory;
52 /******************* utilities *****************************************/
54 public class Exception extends java.lang.Exception {
55 Exception(String details) {
56 super("parse error: " + details + ", found: " + scanner);
58 Exception(String msg, String param) {
59 super(msg + ' ' + param);
63 public static class DefaultEventFactory implements EventFactory {
65 public Event createEvent (ScriptElement parent, String id, List<String> args, int line) {
66 return new Event(parent, id, args.toArray(new String[args.size()]), line);
70 public ESParser (String fname, EventFactory eFact) throws Exception {
72 eventFactory = eFact != null ? eFact : new DefaultEventFactory();
76 FileReader r = new FileReader(fname);
78 scanner = createScanner(r);
79 scanner.nextToken(); // 1 symbol lookahead
81 } catch (FileNotFoundException fnfx) {
82 throw new Exception("file not found:", fname);
83 } catch (IOException iox) {
84 throw new Exception("error reading: ", fname);
88 public ESParser (String fname) throws Exception {
89 this(fname, new DefaultEventFactory());
92 public ESParser (String name, Reader r) throws Exception {
93 this(name, r, new DefaultEventFactory());
96 public ESParser (String name, Reader r, EventFactory eFact) throws Exception {
102 scanner = createScanner(r);
103 scanner.nextToken(); // 1 symbol lookahead
105 } catch (IOException iox) {
106 throw new Exception("error reading: ", name);
110 StreamTokenizer createScanner (Reader r) {
111 StreamTokenizer s = new StreamTokenizer(r);
113 // disable number parsing, since it doesn't work in the context of string expansion
114 // and we also would have to preserve the number type (int or double)
115 s.ordinaryChars('0','9');
116 s.wordChars('0','9');
117 //s.wordChars('"', '"');
119 // those are used to expand events
120 s.wordChars('[','[');
121 s.wordChars(']',']');
122 s.wordChars('|','|');
123 s.wordChars('-','-');
124 s.wordChars('<','<');
125 s.wordChars('>','>');
127 // those can be part of Event IDs
128 s.wordChars('_','_');
129 s.wordChars('#', '#');
130 s.wordChars('*','*');
131 s.wordChars('@','@');
132 s.wordChars('$','$');
133 s.wordChars(':',':');
134 s.wordChars('~','~');
135 s.wordChars('!', '!');
139 s.slashSlashComments(true);
140 s.slashStarComments(true);
142 s.whitespaceChars(',', ',');
143 s.whitespaceChars(';', ';');
148 void nextToken() throws Exception {
150 if (scanner.nextToken() == StreamTokenizer.TT_EOF) {
153 } catch (IOException iox) {
154 throw new Exception("could not read input", iox.toString());
158 void match (char c) throws Exception {
159 if (scanner.ttype == /*(int)*/c) {
162 throw new Exception("char '" + c + "' expected");
166 boolean isMatch (char c) throws Exception {
167 if (scanner.ttype == c) {
175 boolean isMatch (String word) throws Exception {
176 if (scanner.ttype == StreamTokenizer.TT_WORD) {
177 if (scanner.sval.equals(word)) {
186 String matchKeyword (String key) throws Exception {
187 String s = matchWord();
188 if (!s.equals(key)) {
189 throw new Exception("expected keyword: " + key);
194 String matchWord () throws Exception {
195 if (scanner.ttype == StreamTokenizer.TT_WORD) {
196 String s = scanner.sval;
200 throw new Exception("id or keyword expected");
204 boolean isInt (String s) {
206 return ((c >='0') && (c <= '9'));
209 int isIntMatch(int defaultValue) throws Exception {
210 if ((scanner.ttype == StreamTokenizer.TT_WORD) && isInt(scanner.sval)) {
211 int n = Integer.parseInt(scanner.sval);
219 int matchIntNumber () throws Exception {
220 // Ok, this isn't too effective since our scanner doesn't parse numbers
221 if ((scanner.ttype == StreamTokenizer.TT_WORD) && isInt(scanner.sval)) {
222 int n = Integer.parseInt(scanner.sval);
226 throw new Exception("number expected");
230 /******************* the recursive descend parser *********************/
232 /************************************** grammar ***********************
233 script ::= {section | sequence}.
234 section ::= 'SECTION' ID {',' ID} '{' {sequence} '}'.
235 sequence ::= iteration | selection | event.
236 iteration ::= 'REPEAT' [NUM] '{' {sequence} '}'.
237 selection ::= 'ANY' '{' {event} '}'.
238 event ::= ID ['(' [parameter {',' parameter}] ')'].
241 all parameters are treated as strings, but string literals preserve the double quotes
242 (we need to preserve the token because of argument expansion and numeric type)
244 the event ID can contain '#', '.', ':', '@', '$' '/' and '*' chars (e.g. for further
245 parsing of targets etc.)
248 ***********************************************************************/
251 public Script parse() throws Exception {
252 Script s = new Script();
255 if (isMatch(K_SECTION)) {
265 protected void section (ScriptElementContainer parent) throws Exception {
266 //matchKeyword(K_SECTION);
267 ArrayList<String> ids = new ArrayList<String>();
269 String id = matchWord();
272 while (isMatch(',')) {
277 Section sec = new Section(parent, ids, scanner.lineno());
281 while (!done && (scanner.ttype != '}')) {
289 protected void sequence (ScriptElementContainer parent) throws Exception {
290 if (isMatch(K_REPEAT)) {
292 } else if (isMatch(K_ANY)) {
295 if (scanner.ttype == StreamTokenizer.TT_WORD) {
298 if (scanner.ttype == StreamTokenizer.TT_EOF){
299 done = true; // empty sequence
301 throw new Exception("repetition, alternative or event expected");
308 protected void repetition (ScriptElementContainer parent) throws Exception {
309 //matchKeyword(K_REPEAT);
310 int n = isIntMatch(-1); // default is unbounded
312 Repetition r = new Repetition(parent, n, scanner.lineno());
316 while (!done && (scanner.ttype != '}')) {
323 protected void alternative (ScriptElementContainer parent) throws Exception {
324 //matchKeyword(K_ANY);
326 Alternative a = new Alternative(parent, scanner.lineno());
330 while (!done && (scanner.ttype != '}')) {
337 protected void event (ScriptElementContainer parent) throws Exception {
338 String id = matchWord();
340 ArrayList<String> args = new ArrayList<String>();
342 while (!isMatch(')')) {
344 if (scanner.ttype == StreamTokenizer.TT_WORD) {
345 args.add(scanner.sval);
346 } else if (scanner.ttype == '"'){ // string literal
347 args.add( "\"" + scanner.sval + '"');
354 Event e = eventFactory.createEvent(parent, id, args, scanner.lineno());
358 /********** test functions ************/
361 public static void tokenize (String fname) {
363 ESParser parser = new ESParser(fname);
364 StreamTokenizer s = parser.scanner;
366 while (s.ttype != StreamTokenizer.TT_EOF) {
368 case StreamTokenizer.TT_WORD:
369 System.out.println("WORD: " + s.sval);
372 case StreamTokenizer.TT_NUMBER:
373 System.out.println("NUM: " + s.nval);
377 char c = (char)s.ttype;
378 if (c == '"'){ // string literal
379 System.out.println("STRING: \"" + s.sval + '"');
381 System.out.println("CHAR: " + (char)s.ttype);
386 } catch (Throwable t) {
391 static void showScript (String fname){
393 ESParser parser = new ESParser(fname);
394 Script script = parser.parse();
396 PrintWriter pw = new PrintWriter(System.out, true);
397 pw.println("------------------ script AST:");
400 pw.println("------------------ generated CG sequence:");
402 StringSetGenerator p = new StringSetGenerator();
405 LinkedHashMap<String,ArrayList<CG>> sections = p.getSections();
406 for (Map.Entry<String,ArrayList<CG>> e : sections.entrySet()) {
407 ArrayList<CG> queue = e.getValue();
408 System.out.println(e.getKey() + " {");
409 for (CG cg : queue) {
410 System.out.print(" ");
411 System.out.println(cg);
413 System.out.println("}");
414 System.out.println();
417 /** this only shows the last section
418 List<CG> queue = p.getCGQueue();
419 for (CG cg : queue) {
420 System.out.println(cg);
424 } catch (Throwable t){
430 public static void main(String[] args) {