Index: /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/eval/ExpressionReader.java
===================================================================
--- /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/eval/ExpressionReader.java	(revision 1020)
+++ /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/eval/ExpressionReader.java	(working copy)
@@ -2,12 +2,12 @@
 
 import java.util.Stack;
 
-import uk.me.parabola.log.Logger;
 import static uk.me.parabola.mkgmap.osmstyle.eval.Op.CLOSE_PAREN;
 import static uk.me.parabola.mkgmap.osmstyle.eval.Op.EQUALS;
 import static uk.me.parabola.mkgmap.osmstyle.eval.Op.NOT_EQUALS;
 import static uk.me.parabola.mkgmap.osmstyle.eval.Op.OPEN_PAREN;
 import static uk.me.parabola.mkgmap.osmstyle.eval.Op.VALUE;
+import uk.me.parabola.log.Logger;
 import uk.me.parabola.mkgmap.scan.TokenScanner;
 
 /**
@@ -35,7 +35,7 @@
 				break;
 
 			String val = scanner.nextWord();
-			if ("&|!=~()><".contains(val)) 
+			if ("&|!=~()><".contains(val))
 				saveOp(val);
 			else
 				pushValue(val);
@@ -46,6 +46,9 @@
 			runOp();
 
 		// The stack should contain one entry which is the complete tree
+		if (stack.size() != 1) {
+			throw new SyntaxException(scanner, "Stack size is "+stack.size());
+		}
 		assert stack.size() == 1;
 		return stack.pop();
 	}
@@ -102,7 +105,7 @@
 				log.debug("convert to NOT EXISTS");
 				op = new NotExistsOp();
 				op.setFirst(arg1);
-			} 
+			}
 		}
 		stack.push(op);
 	}
Index: /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/PrependFilter.java
===================================================================
--- /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/PrependFilter.java	(revision 0)
+++ /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/PrependFilter.java	(revision 0)
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2009 Toby Speight
+ *
+ *  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.
+ */
+
+package uk.me.parabola.mkgmap.osmstyle.actions;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Prepend a Garmin magic-character to the value.
+ * TODO: symbolic names?
+ *
+ * @author Toby Speight
+ */
+public class PrependFilter extends ValueFilter {
+	private final String prefix;
+
+	// TODO: runtime select appropriate table
+	private Map<String, String>symbols = symbols_8bit;
+
+	private static final Map<String, String> symbols_6bit;
+	private static final Map<String, String> symbols_8bit;
+
+	static {
+		// Firstly, the symbols common to both encodings
+		symbols_6bit = new HashMap<String, String>();
+		symbols_6bit.put("ele", "\u001f"); // name.height separator
+
+		// Copy to other encoding
+		symbols_8bit = new HashMap<String, String>(symbols_6bit);
+
+		// Now add other symbols
+		symbols_6bit.put("interstate", "\u002a"); // US Interstate
+		symbols_8bit.put("interstate", "\u0001");
+		symbols_6bit.put("shield", "\u002b"); // US Highway shield
+		symbols_8bit.put("shield", "\u0002");
+		symbols_6bit.put("round", "\u002c"); // US Highway round
+		symbols_8bit.put("round", "\u0003");
+		symbols_6bit.put("boxx", "\u002d"); // box with horizontal bands
+		symbols_8bit.put("boxx", "\u0004");
+		symbols_6bit.put("box", "\u002e"); // Square box
+		symbols_8bit.put("box", "\u0005");
+		symbols_6bit.put("oval", "\u002f"); // box with rounded ends
+		symbols_8bit.put("oval", "\u0006");
+	}
+
+	public PrependFilter(String s) {
+		// First, try the lookup table
+		String p = symbols.get(s);
+		if (p == null) {
+			// else, s is a hex constant character number
+			try {
+				p = Character.toString((char)Integer.parseInt(s, 16));
+			} catch (NumberFormatException e) {
+				// failed - use string literally
+				p = s;
+			}
+		}
+		prefix = p;
+	}
+
+	public String doFilter(String value) {
+		return value == null ? null : prefix + value;
+	}
+}
Index: /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/HeightFilter.java
===================================================================
--- /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/HeightFilter.java	(revision 0)
+++ /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/HeightFilter.java	(revision 0)
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2009 Toby Speight
+ *
+ *  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.
+ */
+
+package uk.me.parabola.mkgmap.osmstyle.actions;
+
+/**
+ * A <code>HeightFilter</code> transforms values into Garmin-tagged elevations.
+ *
+ * @author Toby Speight
+ *
+ * @since 2009-04-26
+ */
+public class HeightFilter extends ConvertFilter {
+
+    public HeightFilter(String s) {
+		super(s);
+    }
+
+	public String doFilter(String value) {
+		String s = super.doFilter(value);
+		if (s != null)
+			s = "\u001f" + s;
+		return s;
+	}
+}
Index: /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/SubstitutionFilter.java
===================================================================
--- /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/SubstitutionFilter.java	(revision 0)
+++ /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/SubstitutionFilter.java	(revision 0)
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009 Toby Speight
+ *
+ *  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.
+ */
+package uk.me.parabola.mkgmap.osmstyle.actions;
+
+/**
+ * Perform simple string substition on a value.
+ *
+ * @author Toby Speight
+ */
+public class SubstitutionFilter extends ValueFilter {
+	private final String from;
+	private final String to;
+
+	public SubstitutionFilter(String arg) {
+		int i = arg.indexOf("=>");
+		if (i >= 0) {
+			from = arg.substring(0, i);
+			to = arg.substring(i + 2);
+		} else {
+			from = arg;
+			to = "";
+		}
+	}
+
+	public String doFilter(String value) {
+		if (value == null) return null;
+		if (from == null || to == null)
+			// can't happen!
+			return value;
+		return value.replace(from, to);
+	}
+}
Index: /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/ValueBuilder.java
===================================================================
--- /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/ValueBuilder.java	(revision 1020)
+++ /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/ValueBuilder.java	(working copy)
@@ -1,16 +1,16 @@
 /*
  * Copyright (C) 2008 Steve Ratcliffe
- * 
+ *
  *  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.
- * 
- * 
+ *
+ *
  * Author: Steve Ratcliffe
  * Create date: 02-Dec-2008
  */
@@ -27,6 +27,7 @@
  * Build a value that can have tag values substituted in it.
  *
  * @author Steve Ratcliffe
+ * @author Toby Speight
  */
 public class ValueBuilder {
 
@@ -92,20 +93,21 @@
 			switch (state) {
 			case 0:
 				if (c == '$') {
-					if (text.length() > 0) {
-						items.add(new ValueItem(text.toString()));
-						text.setLength(0);
-					}
 					state = 1;
 				} else
 					text.append(c);
 				break;
 			case 1:
 				if (c == '{') {
+					if (text.length() > 0) {
+						items.add(new ValueItem(text.toString()));
+						text.setLength(0);
+					}
 					tagname = new StringBuilder();
 					state = 2;
 				} else {
 					state = 0;
+					text.append('$');
 					text.append(c);
 				}
 				break;
@@ -154,6 +156,14 @@
 			item.addFilter(new DefaultFilter(arg));
 		} else if (cmd.equals("conv")) {
 			item.addFilter(new ConvertFilter(arg));
+		} else if (cmd.equals("subst")) {
+			item.addFilter(new SubstitutionFilter(arg));
+		} else if (cmd.equals("prefix")) {
+			item.addFilter(new PrependFilter(arg));
+		} else if (cmd.equals("highway-symbol")) {
+			item.addFilter(new HighwaySymbolFilter(arg));
+		} else if (cmd.equals("height")) {
+			item.addFilter(new HeightFilter(arg));
 		}
 	}
 
Index: /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/HighwaySymbolFilter.java
===================================================================
--- /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/HighwaySymbolFilter.java	(revision 0)
+++ /home/tms/maps/mkgmap/trunk/src/uk/me/parabola/mkgmap/osmstyle/actions/HighwaySymbolFilter.java	(revision 0)
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2009 Toby Speight
+ *
+ *  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.
+ */
+
+package uk.me.parabola.mkgmap.osmstyle.actions;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Prepend a Garmin magic-character to the value.
+ * TODO: symbolic names?
+ *
+ * @author Toby Speight
+ */
+public class HighwaySymbolFilter extends ValueFilter {
+	private final String prefix;
+
+	private static final Map<String, String>symbols = new HashMap<String, String>();
+	private static final int MAX_REF_LENGTH = 8; // enough for "A6144(M)" (RIP)
+
+	static {
+		//symbols.put("ele", "\u001f"); // name.height separator
+
+		// Now add other symbols
+		symbols.put("interstate", "\u0001"); // US Interstate
+		symbols.put("shield", "\u0002"); // US Highway shield
+		symbols.put("round", "\u0003"); // US Highway round
+		symbols.put("hbox", "\u0004"); // box with horizontal bands
+		symbols.put("box", "\u0005"); // Square box
+		symbols.put("oval", "\u0006"); // box with rounded ends
+	}
+
+	public HighwaySymbolFilter(String s) {
+		// First, try the lookup table
+		String p = symbols.get(s);
+		if (p == null) {
+			p = "[" + s + "]";
+		}
+		prefix = p;
+	}
+
+	public String doFilter(String value) {
+		if (value == null || value.length() > MAX_REF_LENGTH) return value;
+
+		// is it mostly alphabetic?
+		int alpha_balance = 0;
+		for (char c : value.toCharArray()) {
+			alpha_balance += (Character.isLetter(c)) ? 1 : -1;
+		}
+		if (alpha_balance > 0) return value;
+
+		// remove space if there is exactly one
+		int first_space = value.indexOf(" ");
+		if (first_space >= 0 && value.indexOf(" ", first_space) < 0) {
+			value = value.replace(" ", "");
+		}
+
+		return prefix + value;
+	}
+}
Index: /home/tms/maps/mkgmap/trunk/resources/styles/default/lines
===================================================================
--- /home/tms/maps/mkgmap/trunk/resources/styles/default/lines	(revision 1020)
+++ /home/tms/maps/mkgmap/trunk/resources/styles/default/lines	(working copy)
@@ -8,6 +8,13 @@
 	{ name '${ele|conv:m=>ft}'; }
 	[0x21 resolution 20]
 
+# Set highway names to include the reference if there is one
+highway=motorway {name '${ref|highway-symbol:hbox} ${name}' | '${ref|highway-symbol:hbox}' | '${name}' }
+highway=trunk {name '${ref|highway-symbol:hbox} ${name}' | '${ref|highway-symbol:hbox}' | '${name}' }
+highway=primary {name '${ref|highway-symbol:box} ${name}' | '${ref|highway-symbol:box}' | '${name}' }
+highway=secondary {name '${ref|highway-symbol:oval} ${name}' | '${ref|highway-symbol:oval}' | '${name}' }
+highway=* {name '${ref} ${name}' | '${ref}' | '${name}' }
+
 junction=roundabout & highway=trunk [0x0c road_class=3 road_speed=5 resolution 16]
 junction=roundabout & highway=primary [0x0c road_class=3 road_speed=4 resolution 19]
 junction=roundabout & highway=secondary [0x0c road_class=2 road_speed=3 resolution 20]
@@ -15,8 +22,6 @@
 junction=roundabout & highway=unclassified [0x0c road_class=1 road_speed=2 resolution 21]
 junction=roundabout [0x0c road_class=0 road_speed=1 resolution 21]
 
-# Set highway names to include the reference if there is one
-#highway=* {name '${name} (${ref})' | '${ref}' | '${name}' }
 highway=bridleway {add access = no; add bicycle = yes; add foot = yes} [0x16 road_class=0 road_speed=0 resolution 23]
 highway=byway [0x16 road_class=0 road_speed=0 resolution 23]
 highway=cycleway {add access = no; add bicycle = yes; add foot = yes} [0x16 road_class=0 road_speed=1 resolution 23]
Index: /home/tms/maps/mkgmap/trunk/resources/styles/default/points
===================================================================
--- /home/tms/maps/mkgmap/trunk/resources/styles/default/points	(revision 1020)
+++ /home/tms/maps/mkgmap/trunk/resources/styles/default/points	(working copy)
@@ -99,9 +99,10 @@
 
 # Edge 705 displays 0x650a,0x6511,0x6512,0x6513 as hollow white circles, no menu
 natural=beach [0x6604 resolution 21]
+natural=cave_entrance [0x6601 resolution 19]
 natural=cliff [0x6607 resolution 21]
 natural=glacier [0x650a resolution 21]
-natural=peak [0x6616 resolution 21]
+natural=peak {name '${name|def:}${ele|height:m=>ft|def:}' } [0x6616 resolution 18]
 natural=spring [0x6511 resolution 21]
 #natural=stream [0x6512 resolution 21]
 natural=volcano [0x2c0c resolution 21]
