All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.electric.database.variable.CodeExpression Maven / Gradle / Ivy

/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: CodeExpression.java
 * Written by: Dmitry Nadezhin, Sun Microsystems.
 *
 * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) 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 Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 */
package com.sun.electric.database.variable;

import com.sun.electric.database.EObjectInputStream;
import com.sun.electric.database.EObjectOutputStream;
import com.sun.electric.database.id.IdReader;
import com.sun.electric.database.id.IdWriter;
import com.sun.electric.tool.lang.EvalJavaBsh;
import com.sun.electric.tool.lang.EvalSpice;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.collections.ArrayIterator;

import java.io.IOException;
import java.io.Serializable;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Class encapsulate expression in Java-like or Spice-like syntax
 */
public class CodeExpression implements Serializable {
    // Variable flags in C Electric

    /** variable is interpreted code (with VCODE2) */
    private static final int VCODE1 = 040;
    /** variable is interpreted code (with VCODE1) */
    private static final int VCODE2 = 04000000000;
    /** variable is LISP */
    private static final int VSPICE = VCODE1;
    /** variable is TCL */
    private static final int VTCL = VCODE2;
    /** variable is Java */
    private static final int VJAVA = (VCODE1 | VCODE2);
    private static final HashMap javaExpressions = new HashMap();
    private static final HashMap spiceExpressions = new HashMap();
    private static final HashMap tclExpressions = new HashMap();
    private static long numValueOfs;
    /** For replacing @variable */
    private static final Pattern pPat = Pattern.compile("P\\(\"(\\w+)\"\\)");
    private final Code code;
    private final String expr;
    private final Set depends;
    private final Expr exprTree;
    private final boolean dependsOnEverything;
    private final EvalSpice.ParseException parseException;
    private final String spiceText;
    private final String spiceTextPar;

    /**
     * The type of Code that determines how this Variable's
     * value should be evaluated. If NONE, no evaluation is done.
     */
    public static enum Code {

        /** Indicator that code is in Java. */
        JAVA("Java", VJAVA),
        /** Indicator that code is in Lisp. */
        SPICE("Spice", VSPICE),
        /** Indicator that code is in TCL. */
        TCL("TCL (not avail.)", VTCL),
        /** Indicator that this is not code. */
        NONE("Not Code", 0);
        private final String name;
        private final int cFlags;
        private static final Code[] allCodes = Code.class.getEnumConstants();

        private Code(String name, int cFlags) {
            this.name = name;
            this.cFlags = cFlags;
        }

        /**
         * Method to return the bits value of this code type.
         * This is used in I/O.
         * @return the bits value of this code type.
         */
        public int getCFlags() {
            return cFlags;
        }

        /**
         * Method to return a printable version of this Code.
         * @return a printable version of this Code.
         */
        public String toString() {
            return name;
        }

        /**
         * Method to get an iterator over all Code types.
         */
        public static Iterator getCodes() {
            return ArrayIterator.iterator(allCodes);
        }

        /**
         * Method to convert a bits value to a Code object.
         * @param cBits the bits value (from I/O).
         * @return the Code associated with those bits.
         */
        public static Code getByCBits(int cBits) {
            switch (cBits & (VCODE1 | VCODE2)) {
                case VJAVA:
                    return JAVA;
                case VSPICE:
                    return SPICE;
                case VTCL:
                    return TCL;
                default:
                    return NONE;
            }
        }

        /**
         * Method to get a Code constant by its ordinal number.
         * @param ordinal the ordinal number of this Code constant ( as returned by ordinal()
         * @return the Code associated with this ordinal number.
         */
        public static Code getByOrdinal(int ordinal) {
            return allCodes[ordinal];
        }
    }

    private CodeExpression(String expr, Code code) {
        this.code = code;
        this.expr = expr;
        TreeSet varKeys = new TreeSet();
        String replacedExpr = EvalJavaBsh.replace(expr);
        Matcher pMat = pPat.matcher(replacedExpr);
        while (pMat.find()) {
            String varName = pMat.group(1);
            varKeys.add(Variable.newKey(varName));
        }
        depends = Collections.unmodifiableSet(varKeys);

        String patchedExpr = expr.replace("((Number)@X).doubleValue()", "@X");
        Expr exprTree = null;
        EvalSpice.ParseException parseException = null;
        try {
            switch (code) {
                case JAVA:
                    exprTree = parse(patchedExpr, true);
                    break;
                case SPICE:
                    exprTree = parse(patchedExpr, false);
                    break;
                default:
                    throw new EvalSpice.ParseException("Unsupported code " + code);
            }

        } catch (EvalSpice.ParseException e) {
            parseException = e;
        }
        this.exprTree = exprTree;
        this.parseException = parseException;

        if (parseException != null) {
            dependsOnEverything = true;
            spiceTextPar = spiceText = parseException.getMessage();
        } else {
            dependsOnEverything = exprTree.dependsOnEverything();
            StringBuilder sb = new StringBuilder();
            exprTree.appendText(sb);
            spiceText = new String(sb);
            sb.setLength(0);
            exprTree.appendText(sb, Expr.MIN_PRECEDENCE);
            spiceTextPar = new String(sb);
        }
    }

    private Object writeReplace() {
        return new CodeExpressionKey(this);
    }

    private static class CodeExpressionKey extends EObjectInputStream.Key {

        public CodeExpressionKey() {
        }

        private CodeExpressionKey(CodeExpression ce) {
            super(ce);
        }

        @Override
        public void writeExternal(EObjectOutputStream out, CodeExpression ce) throws IOException {
            out.writeUTF(ce.getExpr());
            out.writeByte(ce.getCode().ordinal());
        }

        @Override
        public CodeExpression readExternal(EObjectInputStream in) throws IOException, ClassNotFoundException {
            String expr = in.readUTF();
            Code code = Code.getByOrdinal(in.readByte());
            return valueOf(expr, code);
        }
    }

    public static synchronized CodeExpression valueOf(String expression, Code code) {
        numValueOfs++;
        HashMap allExpressions;
        switch (code) {
            case JAVA:
                allExpressions = javaExpressions;
                break;
            case SPICE:
                allExpressions = spiceExpressions;
                break;
            case TCL:
                allExpressions = tclExpressions;
                break;
            default:
                throw new IllegalArgumentException("code");
        }
        CodeExpression ce = allExpressions.get(expression);
        if (ce == null) {
            ce = new CodeExpression(expression, code);
            allExpressions.put(expression, ce);
        }
        return ce;
    }

    public Code getCode() {
        return code;
    }

    public boolean isJava() {
        return code == Code.JAVA;
    }

    public String getExpr() {
        return expr;
    }

    public Set dependsOn() {
        return depends;
    }

    public EvalSpice.ParseException getParseException() {
        return parseException;
    }

    public String getSpiceText() {
        return spiceText;
    }

    public String getHSpiceText(boolean inPar) {
        return dependsOnEverything ? null : inPar ? spiceTextPar : spiceText;
    }

    public String getVerilogText() {
        return spiceText;
    }

    public Object eval() {
        if (parseException != null) {
            return parseException.getMessage();
        }
        return exprTree.eval(new EvalContext());
    }

    @Override
    public int hashCode() {
        return expr.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof CodeExpression) {
            CodeExpression that = (CodeExpression) o;
            return this.code == that.code && this.expr.equals(that.expr);
        }
        return false;
    }

    @Override
    public String toString() {
        return expr;
    }

    /**
     * Write this CodeExpression to IdWriter.
     * @param writer where to write.
     */
    public void write(IdWriter writer) throws IOException {
        writer.writeString(expr);
        writer.writeByte((byte) code.ordinal());
    }

    /**
     * Read CodeExpression from SnapshotReader.
     * @param reader from to write.
     */
    public static CodeExpression read(IdReader reader) throws IOException {
        String expr = reader.readString();
        Code code = Code.getByOrdinal(reader.readByte());
        return valueOf(expr, code);
    }

    void check() {
        assert expr != null;
        assert code != null && code != Code.NONE;
    }

    /**
     * Print statistics about CodeExpressions.
     * @param verbose print all CodeExpressions
     */
    public static void printStatistics(boolean verbose) {
        System.out.println((javaExpressions.size() + spiceExpressions.size() + tclExpressions.size())
                + " CodeExpressions after " + numValueOfs + " valueOf calls");
        if (!verbose) {
            return;
        }

        System.out.println(javaExpressions.size() + " java strings");
        for (CodeExpression ce : new TreeMap(javaExpressions).values()) {
            printCE(ce);
        }
        System.out.println(spiceExpressions.size() + " spice strings");
        for (CodeExpression ce : new TreeMap(spiceExpressions).values()) {
            printCE(ce);
        }
        System.out.println(tclExpressions.size() + " tcl strings");
        for (CodeExpression ce : new TreeMap(tclExpressions).values()) {
            printCE(ce);
        }
    }

    private static void printCE(CodeExpression ce) {
        System.out.print("\"" + ce.getExpr() + "\"\t");
        if (ce.dependsOnEverything) {
            System.out.print(" ALL");
        }
        for (Variable.Key varKey : ce.dependsOn()) {
            System.out.print(" " + varKey);
        }
        if (ce.parseException != null) {
            System.out.print(" ? " + ce.parseException.getMessage());
        } else {
            System.out.print(" -> \"" + ce.spiceText + "\"");
        }
        System.out.println();
    }

    private static class EvalContext {

        private Object getDrive() {
            return null;
        }

        private Object subDrive(String instName, String varName) {
            return null;
        }

        private Object get(Variable.Key varKey) {
            return null;
        }
    }

    private static abstract class Expr {

        static final int MIN_PRECEDENCE = 1;
        static final int MAX_PRECEDENCE = EvalSpice.Op.COND.precedence;

        int numSubExprs() {
            return 0;
        }

        Expr getSubExpr(int i) {
            throw new IndexOutOfBoundsException();
        }

        void appendText(StringBuilder sb, int outerPrecedence) {
            if (outerPrecedence < precedence()) {
                sb.append('(');
                appendText(sb);
                sb.append(')');
            } else {
                appendText(sb);
            }
        }

        abstract void appendText(StringBuilder sb);

        int precedence() {
            return MIN_PRECEDENCE;
        }

        boolean dependsOnEverything() {
            for (int i = 0; i < numSubExprs(); i++) {
                if (getSubExpr(i).dependsOnEverything()) {
                    return true;
                }
            }
            return false;
        }

        abstract Object eval(EvalContext context);

        static boolean bool(double d) {
            return d != 0;
        }
    }

    private static class ConstExpr extends Expr {

        private final Object value;

        ConstExpr(Object value) {
            this.value = value;
        }

        void appendText(StringBuilder sb) {
            String s = TextUtils.formatDoublePostFix(((Double) value).doubleValue());
            sb.append(s);
        }

        Object eval(EvalContext context) {
            return value;
        }
    }

    private static class VarExpr extends Expr {

        private final Variable.Key varKey;

        VarExpr(Variable.Key varKey) {
            if (varKey == null) {
                throw new NullPointerException();
            }
            this.varKey = varKey;
        }

        void appendText(StringBuilder sb) {
            String name = varKey.getName();
            if (name.startsWith("ATTR_")) {
                name = name.substring(5);
            }
            sb.append(name);
        }

        Object eval(EvalContext context) {
            return context.get(varKey);
        }
    }

    private static class GetDriveExpr extends Expr {

        GetDriveExpr() {
        }

        void appendText(StringBuilder sb) {
            sb.append("LE.getdrive()");
        }

        boolean dependsOnEverything() {
            return true;
        }

        Object eval(EvalContext context) {
            return context.getDrive();
        }
    }

    private static class SubDriveExpr extends Expr {

        private final String instName, varName;

        SubDriveExpr(String instName, String varName) {
            this.instName = instName;
            this.varName = varName;
        }

        void appendText(StringBuilder sb) {
            sb.append("LE.subdrive(\"" + instName + "\",\"" + varName + "\")");
        }

        boolean dependsOnEverything() {
            return true;
        }

        Object eval(EvalContext context) {
            return context.subDrive(instName, varName);
        }
    }

    private static abstract class UnaryExpr extends Expr {

        final Expr s;

        UnaryExpr(Expr s) {
            this.s = s;
        }

        int numSubExprs() {
            return 1;
        }

        Expr getSubExpr(int i) {
            if (i == 0) {
                return s;
            }
            return super.getSubExpr(i);
        }

        Object eval(EvalContext context) {
            double v = ((Number) s.eval(context)).doubleValue();
            return Double.valueOf(apply(v));
        }

        abstract double apply(double v);
    }

    private static class UnaryOpExpr extends UnaryExpr {

        private static final String opName = EvalSpice.Op.MINUS.name;
        private static final int opPrecedence = MIN_PRECEDENCE;

        UnaryOpExpr(Expr s) {
            super(s);
        }

        void appendText(StringBuilder sb) {
            sb.append(opName);
            s.appendText(sb, opPrecedence);
        }

        int precedence() {
            return opPrecedence;
        }

        double apply(double v) {
            return -v;
        }
    }

    private static class UnaryFunExpr extends UnaryExpr {

        enum Fun {

            sin, abs, sqrt, int_;

            @Override
            public String toString() {
                return this == int_ ? "int" : super.toString();
            }
        };
        private final Fun fun;

        UnaryFunExpr(Fun fun, Expr s) {
            super(s);
            this.fun = fun;
        }

        void appendText(StringBuilder sb) {
            sb.append(fun);
            sb.append('(');
            s.appendText(sb, MAX_PRECEDENCE);
            sb.append(')');
        }

        double apply(double v) {
            switch (fun) {
                case sin:
                    return Math.sin(v);
                case abs:
                    return Math.abs(v);
                case sqrt:
                    return Math.sqrt(v);
                case int_:
                    return (int) v;
            }
            throw new AssertionError();
        }
    }

    private static abstract class BinaryExpr extends Expr {

        final Expr ls, rs;

        BinaryExpr(Expr ls, Expr rs) {
            this.ls = ls;
            this.rs = rs;
        }

        int numSubExprs() {
            return 2;
        }

        Expr getSubExpr(int i) {
            if (i == 0) {
                return ls;
            }
            if (i == 1) {
                return rs;
            }
            return super.getSubExpr(i);
        }

        Object eval(EvalContext context) {
            double lv = ((Double) ls.eval(context)).doubleValue();
            double rv = ((Double) rs.eval(context)).doubleValue();
            return Double.valueOf(apply(lv, rv));
        }

        abstract double apply(double lv, double rv);
    }

    private static class BinaryOpExpr extends BinaryExpr {

        private final EvalSpice.Op op;

        BinaryOpExpr(Expr ls, EvalSpice.Op op, Expr rs) {
            super(ls, rs);
            this.op = op;
        }

        void appendText(StringBuilder sb) {
            ls.appendText(sb, op.precedence);
            sb.append(op.name);
            rs.appendText(sb, op.precedence - 1);
        }

        int precedence() {
            return op.precedence;
        }

        double apply(double lv, double rv) {
            if (op == EvalSpice.Op.MULT) {
                return lv * rv;
            }
            if (op == EvalSpice.Op.DIV) {
                return lv / rv;
            }
            if (op == EvalSpice.Op.PLUS) {
                return lv + rv;
            }
            if (op == EvalSpice.Op.MINUS) {
                return lv - rv;
            }
            if (op == EvalSpice.Op.LT) {
                return valueOf(lv < rv);
            }
            if (op == EvalSpice.Op.LTOE) {
                return valueOf(lv <= rv);
            }
            if (op == EvalSpice.Op.GT) {
                return valueOf(lv > rv);
            }
            if (op == EvalSpice.Op.GTOE) {
                return valueOf(lv >= rv);
            }
            if (op == EvalSpice.Op.EQ) {
                return valueOf(lv == rv);
            }
            if (op == EvalSpice.Op.NE) {
                return valueOf(lv != rv);
            }
            if (op == EvalSpice.Op.LAND) {
                return valueOf(bool(lv) && bool(rv));
            }
            if (op == EvalSpice.Op.LOR) {
                return valueOf(bool(lv) || bool(rv));
            }
            throw new AssertionError();
        }

        private static double valueOf(boolean b) {
            return b ? 1 : 0;
        }
    }

    private static class BinaryFunExpr extends BinaryExpr {

        enum Fun {

            min, max
        };
        private final Fun fun;

        BinaryFunExpr(Fun fun, Expr ls, Expr rs) {
            super(ls, rs);
            this.fun = fun;
        }

        void appendText(StringBuilder sb) {
            sb.append(fun);
            sb.append('(');
            ls.appendText(sb, MAX_PRECEDENCE);
            sb.append(',');
            rs.appendText(sb, MAX_PRECEDENCE);
            sb.append(')');
        }

        double apply(double lv, double rv) {
            switch (fun) {
                case min:
                    return Math.min(lv, rv);
                case max:
                    return Math.max(lv, rv);
            }
            throw new AssertionError();
        }
    }

    private static class IfThenElseExpr extends Expr {

        private static final int precedence = MAX_PRECEDENCE;
        final Expr condS, thenS, elseS;

        IfThenElseExpr(Expr condS, Expr thenS, Expr elseS) {
            this.condS = condS;
            this.thenS = thenS;
            this.elseS = elseS;
        }

        int numSubExprs() {
            return 3;
        }

        Expr getSubExpr(int i) {
            if (i == 0) {
                return condS;
            }
            if (i == 1) {
                return thenS;
            }
            if (i == 2) {
                return elseS;
            }
            return super.getSubExpr(i);
        }

        void appendText(StringBuilder sb) {
            condS.appendText(sb, precedence - 1);
            sb.append('?');
            thenS.appendText(sb, precedence - 1);
            sb.append(':');
            elseS.appendText(sb, precedence);
        }

        int precedence() {
            return MAX_PRECEDENCE;
        }

        Object eval(EvalContext context) {
            boolean condV = bool(((Double) condS.eval(context)).doubleValue());
            double v = ((Double) (condV ? thenS : elseS).eval(context)).doubleValue();
            return Double.valueOf(v);
        }
    }

    static Expr parse(String expr, boolean isJava) throws EvalSpice.ParseException {
        return new ParseSpice_(expr, isJava).parse();
    }

    static class ParseSpice_ {

        private String expr;
        private boolean isJava;
        private StringReader reader;
        private StreamTokenizer tokenizer;
        EvalSpice.Op op;

        private ParseSpice_(String expr, boolean isJava) {
            this.expr = expr;
            this.isJava = isJava;
            reader = new StringReader(expr);
            tokenizer = new StreamTokenizer(reader);
            tokenizer.parseNumbers();
            tokenizer.ordinaryChar('(');
            tokenizer.ordinaryChar(')');
            tokenizer.ordinaryChar('*');
            tokenizer.ordinaryChar('/');
            tokenizer.ordinaryChar('+');
            tokenizer.ordinaryChar('-');
            tokenizer.ordinaryChar('<');
            tokenizer.ordinaryChar('=');
            tokenizer.ordinaryChar('>');
            tokenizer.ordinaryChar('!');
            tokenizer.ordinaryChar('?');
            tokenizer.ordinaryChar(':');
            tokenizer.ordinaryChar('@');
            tokenizer.quoteChar('"');
            tokenizer.wordChars('_', '_');
        }

        private Expr parse() throws EvalSpice.ParseException {
            try {
                nextToken();
                Expr expr = evalEq();
                assert op == null;
                switch (tokenizer.ttype) {
                    case StreamTokenizer.TT_EOF:
                        return expr;
                    case StreamTokenizer.TT_EOL:
                        throw new EvalSpice.ParseException("Multiline expression");
                    case StreamTokenizer.TT_NUMBER:
                    case StreamTokenizer.TT_WORD:
                    case '(':
                        throw new EvalSpice.ParseException("Two operands with no operator");
                    default:
                        throw new EvalSpice.ParseException("Unexpected character " + (char) tokenizer.ttype);
                }
            } catch (IOException e) {
                throw new EvalSpice.ParseException(e.getMessage());
            } catch (EvalSpice.ParseException e) {
                try {
                    long left = reader.skip(Long.MAX_VALUE);
                    int pos = expr.length() - (int) left;
                    throw new EvalSpice.ParseException(expr.substring(0, pos) + "<" + e.getMessage() + ">" + expr.substring(pos));
                } catch (IOException e2) {
                }
            }
            throw new AssertionError();
        }

        private Expr evalEq() throws IOException, EvalSpice.ParseException {
            return evalEq(Expr.MAX_PRECEDENCE);
        }

        /**
         * Evaluate an expression
         * @return the evaluated expression
         * @throws IOException
         * @throws ParseException
         */
        private Expr evalEq(int outerPrecedence) throws IOException, EvalSpice.ParseException {
            boolean unaryMinus = false;
            if (op == EvalSpice.Op.MINUS) {
                unaryMinus = true;
                nextToken();
            }
            Expr e;
            if (tokenizer.ttype == '(') {
                nextToken();
                e = evalEq();
                expect(')');
            } else if (tokenizer.ttype == StreamTokenizer.TT_NUMBER) {
                e = parseNumber();
            } else if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
                e = parseWord();
            } else if (tokenizer.ttype == '@') {
                if (nextToken() != StreamTokenizer.TT_WORD) {
                    throw new EvalSpice.ParseException("Bad name after @");
                }
                e = new VarExpr(Variable.newKey("ATTR_" + tokenizer.sval));
                nextToken();
            } else if (op != null) {
                throw new EvalSpice.ParseException("Operator " + op + " with no left hand operand");
            } else {
                throw new EvalSpice.ParseException("Expected identifier");
            }
            if (unaryMinus) {
                e = new UnaryOpExpr(e);
            }

            for (;;) {
                if (op == null || outerPrecedence < op.precedence) {
                    return e;
                }
                if (op == EvalSpice.Op.COND) {
                    nextToken();
                    Expr thenE = evalEq(Expr.MAX_PRECEDENCE - 1);
                    expect(':');
                    Expr elseE = evalEq();
                    return new IfThenElseExpr(e, thenE, elseE);
                }
                EvalSpice.Op myOp = op;
                assert outerPrecedence >= myOp.precedence;
                nextToken();
                Expr e2 = evalEq(myOp.precedence - 1);
                e = new BinaryOpExpr(e, myOp, e2);
            }
        }

        /**
         * Parse a number. A number may have the format:
         * 

* number[g|meg|k|m|u|n|p|f] *

* number(e)[-]number * @return * @throws IOException * @throws ParseException */ private ConstExpr parseNumber() throws IOException, EvalSpice.ParseException { assert tokenizer.ttype == StreamTokenizer.TT_NUMBER; double val = tokenizer.nval; // peek ahead to check if exponential, or multiplier tokenizer.ordinaryChar('e'); tokenizer.ordinaryChar('E'); int tt = tokenizer.nextToken(); if (tt == 'e' || tt == 'E') { tt = tokenizer.nextToken(); boolean minus = false; if (tt == '-') { minus = true; tt = tokenizer.nextToken(); } if (tt == StreamTokenizer.TT_NUMBER) { double exp = tokenizer.nval; if (minus) { exp = -1.0 * exp; } val = val * Math.pow(10, exp); } else { throw new EvalSpice.ParseException("Invalid token"); } } else if (tt == StreamTokenizer.TT_WORD) { if (tokenizer.sval.equalsIgnoreCase("g")) { val = val * 1e9; } else if (tokenizer.sval.equalsIgnoreCase("meg")) { val = val * 1e6; } else if (tokenizer.sval.equalsIgnoreCase("k")) { val = val * 1e3; } else if (tokenizer.sval.equalsIgnoreCase("m")) { val = val * 1e-3; } else if (tokenizer.sval.equalsIgnoreCase("u")) { val = val * 1e-6; } else if (tokenizer.sval.equalsIgnoreCase("n")) { val = val * 1e-9; } else if (tokenizer.sval.equalsIgnoreCase("p")) { val = val * 1e-12; } else if (tokenizer.sval.equalsIgnoreCase("f")) { val = val * 1e-15; } else { throw new EvalSpice.ParseException("Invalid token"); } } else { tokenizer.pushBack(); } tokenizer.wordChars('e', 'e'); tokenizer.wordChars('E', 'E'); nextToken(); return new ConstExpr(new Double(val)); } private Expr parseWord() throws IOException, EvalSpice.ParseException { assert tokenizer.ttype == StreamTokenizer.TT_WORD; String id = tokenizer.sval; for (UnaryFunExpr.Fun fun : UnaryFunExpr.Fun.class.getEnumConstants()) { if (!id.equalsIgnoreCase(fun.toString()) && !id.equals("Math." + fun.toString())) { continue; } nextToken(); expect('('); Expr arg = evalEq(); expect(')'); return new UnaryFunExpr(fun, arg); } for (BinaryFunExpr.Fun fun : BinaryFunExpr.Fun.class.getEnumConstants()) { if (!id.equalsIgnoreCase(fun.toString()) && !id.equals("Math." + fun.toString())) { continue; } nextToken(); expect('('); Expr arg1 = evalEq(); expect(','); Expr arg2 = evalEq(); expect(')'); return new BinaryFunExpr(fun, arg1, arg2); } if (/*isJava &&*/id.equals("P")) { nextToken(); expect('('); if (tokenizer.ttype != '"') { throw new EvalSpice.ParseException("Bad name after @"); } Variable.Key varKey = Variable.newKey(tokenizer.sval); nextToken(); expect(')'); return new VarExpr(varKey); } if (isJava && id.equals("LE.getdrive")) { nextToken(); expect('('); expect(')'); return new GetDriveExpr(); } if (isJava && id.equals("LE.subdrive")) { nextToken(); expect('('); if (tokenizer.ttype != '"') { throw new EvalSpice.ParseException("Bad name after subdrive"); } String instName = tokenizer.sval; nextToken(); expect(','); if (tokenizer.ttype != '"') { throw new EvalSpice.ParseException("Bad name after subdrive"); } String varName = tokenizer.sval; nextToken(); expect(')'); return new SubDriveExpr(instName, varName); } Variable.Key attrKey = Variable.newKey("ATTR_" + tokenizer.sval); nextToken(); return new VarExpr(attrKey); } private void expect(int token) throws IOException, EvalSpice.ParseException { if (tokenizer.ttype != token) { throw new EvalSpice.ParseException("Expected token " + (char) token); } nextToken(); } private int nextToken() throws IOException, EvalSpice.ParseException { switch (tokenizer.nextToken()) { case '*': op = EvalSpice.Op.MULT; break; case '/': op = EvalSpice.Op.DIV; break; case '+': op = EvalSpice.Op.PLUS; break; case '-': op = EvalSpice.Op.MINUS; break; case '<': op = EvalSpice.Op.LT; if (tokenizer.nextToken() == '=') { op = EvalSpice.Op.LTOE; } else { tokenizer.pushBack(); } break; case '>': op = EvalSpice.Op.GT; if (tokenizer.nextToken() == '=') { op = EvalSpice.Op.GTOE; } else { tokenizer.pushBack(); } break; case '=': op = EvalSpice.Op.EQ; if (tokenizer.nextToken() != '=') { throw new EvalSpice.ParseException("Expected token =="); } break; case '!': op = EvalSpice.Op.NE; if (tokenizer.nextToken() != '=') { throw new EvalSpice.ParseException("Expected token !="); } break; case '?': op = EvalSpice.Op.COND; break; case StreamTokenizer.TT_EOL: throw new EvalSpice.ParseException("Multiline expression"); case ':': case '(': case ')': case ',': case '@': case '"': case StreamTokenizer.TT_NUMBER: case StreamTokenizer.TT_WORD: case StreamTokenizer.TT_EOF: op = null; break; default: throw new EvalSpice.ParseException("Illegal character " + (char) tokenizer.ttype); } return tokenizer.ttype; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy