/* * xtc - The eXTensible Compiler * Copyright (C) 2004-2007 Robert Grimm * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.parser; import java.util.ArrayList; import java.util.List; import xtc.util.Utilities; /** * A semantic action. * * @author Robert Grimm * @version $Revision: 1.22 $ */ public class Action extends Element { /** * The list of source lines for the semantic action. Note that each * source line is a string without the terminating end-of-line * character(s). */ public final List<String> code; /** * The list of indentation levels, one per line of code. Note that * this list must be at least as long as the list of source lines. */ public final List<Integer> indent; /** * Create a new action with the specified code. The specified * string is broken up into individual lines, removing all * end-of-line characters along the way. * * @param s The code as a string. * @param indent The list of indentation levels, one per line * in the specified string. * @throws IllegalArgumentException * Signals that <code>indent</code> is too short. */ public Action(String s, List<Integer> indent) { String[] ss = Utilities.SPACE_NEWLINE_SPACE.split(s); this.indent = indent; if (indent.size() < ss.length) { throw new IllegalArgumentException("List of indentation levels too short"); } // Trim lines and eliminate empty lines at the beginning and end. if (0 < ss.length) { // We only need to trim the first and last line. ss[0] = ss[0].trim(); if (1 != ss.length) { ss[ss.length-1] = ss[ss.length-1].trim(); } } // Find empty lines at beginning and end. int start = ss.length; int end = ss.length-1; for (int i=0; i<ss.length; i++) { if (! "".equals(ss[i])) { start = i; break; } } for (int i=ss.length-1; i>=0; i--) { if (! "".equals(ss[i])) { end = i; break; } } // Remove empty lines from beginning and end. int size = indent.size(); for (int i=0; i<start; i++) { indent.remove(0); } for (int i=end+1; i<size; i++) { indent.remove(indent.size()-1); } code = new ArrayList<String>(end - start + 1); for (int i=start; i<=end; i++) { code.add(ss[i]); } } /** * Create a new action with the specified code. * * @param code The code as a list source lines. * @param indent The corresponding indentation levels. * @throws IllegalArgumentException * Signals that the number of code lines is inconsistent with the * number of indentation levels. */ public Action(List<String> code, List<Integer> indent) { if (indent.size() != code.size()) { throw new IllegalArgumentException("Number of code lines and " + "indentation levels inconsistent"); } this.code = code; this.indent = indent; } public Tag tag() { return Tag.ACTION; } /** * Add the specified action to this action. * * @param a The action to add. */ public void add(Action a) { code.addAll(a.code); indent.addAll(a.indent); } /** * Determine whether this action sets the {@link CodeGenerator#VALUE * semantic value}. This method implements a conservative * approximation by searching for occurrences of the corresponding * variable name. * * @return <code>true</code> if this action sets the semantic value. */ public boolean setsValue() { for (String s : code) { if (-1 != s.indexOf(CodeGenerator.VALUE)) { return true; } } return false; } public int hashCode() { return code.hashCode(); } public boolean equals(Object o) { if (this == o) return true; if (! (o instanceof Action)) return false; Action other = (Action)o; if (! code.equals(other.code)) return false; return indent.equals(other.indent); } }