/* * Copyright (c) 2008, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ package org.contikios.cooja.plugins; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.script.ScriptException; import org.apache.log4j.Logger; import org.contikios.cooja.Simulation; public class ScriptParser { private static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(ScriptParser.class); private long timeoutTime = -1; private String timeoutCode = null; private String code = null; public ScriptParser(String code) throws ScriptSyntaxErrorException { code = fixNewlines(code); code = stripMultiLineComments(code); code = stripSingleLineComments(code); code = parseTimeout(code); code = parseTimeoutWithAction(code); code = replaceYieldThenWaitUntils(code); code = replaceYields(code); code = replaceWaitUntils(code); this.code = code; } private String fixNewlines(String code) { code = code.replaceAll("\r\n", "\n"); code = "\n" + code + "\n"; return code; } private String stripSingleLineComments(String code) { /* TODO Handle strings */ Pattern pattern = Pattern.compile("//.*\n"); Matcher matcher = pattern.matcher(code); code = matcher.replaceAll("\n"); return code; } private String stripFirstComment(String code) { int first = code.indexOf('"'); if (first < 0) { return code; } int second = code.indexOf('"', first+1); code = code.substring(0, first) + code.substring(second+1, code.length()); return code; } private String stripMultiLineComments(String code) { /* TODO Handle strings */ Pattern pattern = Pattern.compile("/\\*([^*]|[\n]|(\\*+([^*/]|[\n])))*\\*+/"); Matcher matcher = pattern.matcher(code); while (matcher.find()) { String match = matcher.group(); int newLines = match.split("\n").length; String replacement = ""; for (int i=0; i < newLines; i++) { replacement += "\n"; } code = matcher.replaceFirst(replacement); matcher.reset(code); } return code; } private String parseTimeout(String code) throws ScriptSyntaxErrorException { Pattern pattern = Pattern.compile( "TIMEOUT\\(" + "([0-9]+)" /* timeout */ + "\\)" ); Matcher matcher = pattern.matcher(code); if (!matcher.find()) { return code; } if (timeoutTime > 0) { throw new ScriptSyntaxErrorException("Only one timeout handler allowed"); } timeoutTime = Long.parseLong(matcher.group(1))*Simulation.MILLISECOND; timeoutCode = ";"; matcher.reset(code); code = matcher.replaceFirst(";"); matcher.reset(code); if (matcher.find()) { throw new ScriptSyntaxErrorException("Only one timeout handler allowed"); } return code; } private String parseTimeoutWithAction(String code) throws ScriptSyntaxErrorException { Pattern pattern = Pattern.compile( "TIMEOUT\\(" + "([0-9]+)" /* timeout */ + "[\\s]*,[\\s]*" + "(.*)" /* code */ + "\\)" ); Matcher matcher = pattern.matcher(code); if (!matcher.find()) { return code; } if (timeoutTime > 0) { throw new ScriptSyntaxErrorException("Only one timeout handler allowed"); } timeoutTime = Long.parseLong(matcher.group(1))*Simulation.MILLISECOND; timeoutCode = matcher.group(2); matcher.reset(code); code = matcher.replaceFirst(";"); matcher.reset(code); if (matcher.find()) { throw new ScriptSyntaxErrorException("Only one timeout handler allowed"); } return code; } private String replaceYields(String code) throws ScriptSyntaxErrorException { Pattern pattern = Pattern.compile( "YIELD\\(\\)" ); return pattern.matcher(code).replaceAll("SCRIPT_SWITCH()"); } private String replaceYieldThenWaitUntils(String code) throws ScriptSyntaxErrorException { Pattern pattern = Pattern.compile( "YIELD_THEN_WAIT_UNTIL\\(" + "(.*)" /* expression */ + "\\)" ); Matcher matcher = pattern.matcher(code); while (matcher.find()) { code = matcher.replaceFirst( "YIELD(); WAIT_UNTIL(" + matcher.group(1) + ")"); matcher.reset(code); } return code; } private String replaceWaitUntils(String code) throws ScriptSyntaxErrorException { Pattern pattern = Pattern.compile( "WAIT_UNTIL\\(" + "(.*)" /* expression */ + "\\)" ); Matcher matcher = pattern.matcher(code); while (matcher.find()) { code = matcher.replaceFirst( "while (!(" + matcher.group(1) + ")) { " + " SCRIPT_SWITCH(); " + "}"); matcher.reset(code); } return code; } public String getJSCode() { return getJSCode(code, timeoutCode); } public static String getJSCode(String code, String timeoutCode) { return "timeout_function = null; " + "function run() { " + "SEMAPHORE_SIM.acquire(); " + "SEMAPHORE_SCRIPT.acquire(); " + /* STARTUP BLOCKS HERE! */ "if (SHUTDOWN) { SCRIPT_KILL(); } " + "if (TIMEOUT) { SCRIPT_TIMEOUT(); } " + "msg = new java.lang.String(msg); " + "node.setMoteMsg(mote, msg); " + code + "\n" + "\n" + "while (true) { SCRIPT_SWITCH(); } " /* SCRIPT ENDED */+ "};" + "\n" + "function GENERATE_MSG(time, msg) { " + " log.generateMessage(time, msg); " + "};\n" + "\n" + "function SCRIPT_KILL() { " + " SEMAPHORE_SIM.release(100); " + " throw('test script killed'); " + "};\n" + "\n" + "function SCRIPT_TIMEOUT() { " + timeoutCode + "; " + " if (timeout_function != null) { timeout_function(); } " + " log.log('TEST TIMEOUT\\n'); " + " log.testFailed(); " + " while (!SHUTDOWN) { " + " SEMAPHORE_SIM.release(); " + " SEMAPHORE_SCRIPT.acquire(); " /* SWITCH BLOCKS HERE! */ + " } " + " SCRIPT_KILL(); " + "};\n" + "\n" + "function SCRIPT_SWITCH() { " + " SEMAPHORE_SIM.release(); " + " SEMAPHORE_SCRIPT.acquire(); " /* SWITCH BLOCKS HERE! */ + " if (SHUTDOWN) { SCRIPT_KILL(); } " + " if (TIMEOUT) { SCRIPT_TIMEOUT(); } " + " msg = new java.lang.String(msg); " + " node.setMoteMsg(mote, msg); " + "};\n" + "\n" + "function write(mote,msg) { " + " mote.getInterfaces().getLog().writeString(msg); " + "};\n"; } public long getTimeoutTime() { return timeoutTime; } public class ScriptSyntaxErrorException extends ScriptException { public ScriptSyntaxErrorException(String msg) { super(msg); } } }