diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Parser.java b/src/main/java/de/neemann/digital/hdl/hgs/Parser.java index a6a84dc05..6022dd47e 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Parser.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Parser.java @@ -56,8 +56,8 @@ public class Parser { String text = tok.readText(); if (text.length() > 0) s.add(c -> c.print(text)); - while (!isToken(EOF)) { - if (isToken(STATIC)) { + while (!nextIs(EOF)) { + if (nextIs(STATIC)) { Statement stat = parseStatement(); try { stat.execute(staticContext); @@ -81,51 +81,57 @@ public class Parser { return parseStatement(true); } - private Statement parseStatement(boolean semicolon) throws IOException, ParserException { - if (isToken(IDENT)) { + /** + * If 'isRealStatement' is false, the statement is parsed like an expression. + * This mode is needed to implement the 'for' loop. In a C style for loop the pre and the + * post code are expressions, which modify state, which is not supported by HGS. In the HGS + * for loop the pre and the post code are statements where the semicolon at the end is omitted. + */ + private Statement parseStatement(boolean isRealStatement) throws IOException, ParserException { + if (nextIs(IDENT)) { Reference ref = parseReference(tok.getIdent()); - if (isToken(EQUAL)) { + if (nextIs(EQUAL)) { Expression val = parseExpression(); - if (semicolon) expect(SEMICOLON); + if (isRealStatement) expect(SEMICOLON); return c -> ref.set(c, val.value(c)); - } else if (isToken(ADD)) { + } else if (nextIs(ADD)) { expect(ADD); - if (semicolon) expect(SEMICOLON); + if (isRealStatement) expect(SEMICOLON); return c -> ref.set(c, Value.toLong(ref.get(c)) + 1); - } else if (isToken(SUB)) { + } else if (nextIs(SUB)) { expect(SUB); - if (semicolon) expect(SEMICOLON); + if (isRealStatement) expect(SEMICOLON); return c -> ref.set(c, Value.toLong(ref.get(c)) - 1); - } else if (isToken(SEMICOLON)) { + } else if (nextIs(SEMICOLON)) { return ref::get; } else throw newUnexpectedToken(tok.next()); - } else if (isToken(CODEEND)) { + } else if (nextIs(CODEEND)) { String str = tok.readText(); return c -> c.print(str); - } else if (isToken(EQUAL)) { + } else if (nextIs(EQUAL)) { Expression exp = parseExpression(); if (tok.peek() != CODEEND) expect(SEMICOLON); return c -> c.print(exp.value(c).toString()); - } else if (isToken(PRINT)) { + } else if (nextIs(PRINT)) { expect(OPEN); ArrayList args = parseArgList(); - if (semicolon) expect(SEMICOLON); + if (isRealStatement) expect(SEMICOLON); return c -> { for (Expression e : args) c.print(e.value(c).toString()); }; - } else if (isToken(PRINTF)) { + } else if (nextIs(PRINTF)) { expect(OPEN); ArrayList args = parseArgList(); - if (semicolon) expect(SEMICOLON); + if (isRealStatement) expect(SEMICOLON); return c -> c.print(FunctionFormat.format(c, args)); - } else if (isToken(IF)) { + } else if (nextIs(IF)) { expect(OPEN); Expression cond = parseExpression(); expect(CLOSE); Statement ifPart = parseStatement(); - if (isToken(ELSE)) { + if (nextIs(ELSE)) { Statement elsePart = parseStatement(); return c -> { if (Value.toBool(cond.value(c))) @@ -138,14 +144,13 @@ public class Parser { if (Value.toBool(cond.value(c))) ifPart.execute(c); }; - - } else if (isToken(FOR)) { + } else if (nextIs(FOR)) { expect(OPEN); - Statement init = parseStatement(false); + Statement init = parseStatement(false); // parse like an expression expect(SEMICOLON); Expression cond = parseExpression(); expect(SEMICOLON); - Statement inc = parseStatement(false); + Statement inc = parseStatement(false); // parse like an expression expect(CLOSE); Statement inner = parseStatement(); return c -> { @@ -155,16 +160,24 @@ public class Parser { inc.execute(c); } }; - } else if (isToken(OPENBRACE)) { + } else if (nextIs(WHILE)) { + expect(OPEN); + Expression cond = parseExpression(); + expect(CLOSE); + Statement inner = parseStatement(); + return c -> { + while (Value.toBool(cond.value(c))) inner.execute(c); + }; + } else if (nextIs(OPENBRACE)) { Statements s = new Statements(); - while (!isToken(CLOSEDBRACE)) + while (!nextIs(CLOSEDBRACE)) s.add(parseStatement()); return s.optimize(); - } else if (isToken(PANIC)) { + } else if (nextIs(PANIC)) { expect(OPEN); Expression message = parseExpression(); expect(CLOSE); - if (semicolon) expect(SEMICOLON); + if (isRealStatement) expect(SEMICOLON); return c -> { throw new HGSEvalException(message.value(c).toString()); }; @@ -174,9 +187,9 @@ public class Parser { private ArrayList parseArgList() throws IOException, ParserException { ArrayList args = new ArrayList<>(); - if (!isToken(CLOSE)) { + if (!nextIs(CLOSE)) { args.add(parseExpression()); - while (isToken(COMMA)) + while (nextIs(COMMA)) args.add(parseExpression()); expect(CLOSE); } @@ -186,14 +199,14 @@ public class Parser { private Reference parseReference(String var) throws IOException, ParserException { Reference r = new ReferenceToVar(var); while (true) { - if (isToken(OPENSQUARE)) { + if (nextIs(OPENSQUARE)) { Expression index = parseExpression(); expect(CLOSEDSQUARE); r = new ReferenceToArray(r, index); - } else if (isToken(OPEN)) { + } else if (nextIs(OPEN)) { ArrayList args = parseArgList(); r = new ReferenceToFunc(r, args); - } else if (isToken(DOT)) { + } else if (nextIs(DOT)) { expect(IDENT); r = new ReferenceToStruct(r, tok.getIdent()); } else @@ -201,7 +214,7 @@ public class Parser { } } - private boolean isToken(Tokenizer.Token t) throws IOException { + private boolean nextIs(Tokenizer.Token t) throws IOException { if (tok.peek() == t) { tok.next(); return true; @@ -247,7 +260,7 @@ public class Parser { private Expression parseExpression() throws IOException, ParserException { Expression ac = parseLessEquals(); - while (isToken(Tokenizer.Token.LESS)) { + while (nextIs(Tokenizer.Token.LESS)) { Expression a = ac; Expression b = parseLessEquals(); ac = c -> Value.toLong(a.value(c)) < Value.toLong(b.value(c)); @@ -257,7 +270,7 @@ public class Parser { private Expression parseLessEquals() throws IOException, ParserException { Expression ac = parseGreater(); - while (isToken(Tokenizer.Token.LESSEQUAL)) { + while (nextIs(Tokenizer.Token.LESSEQUAL)) { Expression a = ac; Expression b = parseGreater(); ac = c -> Value.toLong(a.value(c)) <= Value.toLong(b.value(c)); @@ -267,7 +280,7 @@ public class Parser { private Expression parseGreater() throws IOException, ParserException { Expression ac = parseGreaterEquals(); - while (isToken(Tokenizer.Token.GREATER)) { + while (nextIs(Tokenizer.Token.GREATER)) { Expression a = ac; Expression b = parseGreaterEquals(); ac = c -> Value.toLong(a.value(c)) > Value.toLong(b.value(c)); @@ -277,7 +290,7 @@ public class Parser { private Expression parseGreaterEquals() throws IOException, ParserException { Expression ac = parseEquals(); - while (isToken(Tokenizer.Token.GREATEREQUAL)) { + while (nextIs(Tokenizer.Token.GREATEREQUAL)) { Expression a = ac; Expression b = parseEquals(); ac = c -> Value.toLong(a.value(c)) >= Value.toLong(b.value(c)); @@ -287,7 +300,7 @@ public class Parser { private Expression parseEquals() throws IOException, ParserException { Expression ac = parseNotEquals(); - while (isToken(Tokenizer.Token.EQUAL)) { + while (nextIs(Tokenizer.Token.EQUAL)) { Expression a = ac; Expression b = parseNotEquals(); ac = c -> Value.equals(a.value(c), b.value(c)); @@ -297,7 +310,7 @@ public class Parser { private Expression parseNotEquals() throws IOException, ParserException { Expression ac = parseOR(); - while (isToken(Tokenizer.Token.NOTEQUAL)) { + while (nextIs(Tokenizer.Token.NOTEQUAL)) { Expression a = ac; Expression b = parseOR(); ac = c -> !Value.equals(a.value(c), b.value(c)); @@ -307,7 +320,7 @@ public class Parser { private Expression parseOR() throws IOException, ParserException { Expression ac = parseXOR(); - while (isToken(Tokenizer.Token.OR)) { + while (nextIs(Tokenizer.Token.OR)) { Expression a = ac; Expression b = parseXOR(); ac = c -> Value.or(a.value(c), b.value(c)); @@ -317,7 +330,7 @@ public class Parser { private Expression parseXOR() throws IOException, ParserException { Expression ac = parseAND(); - while (isToken(Tokenizer.Token.XOR)) { + while (nextIs(Tokenizer.Token.XOR)) { Expression a = ac; Expression b = parseAND(); ac = c -> Value.xor(a.value(c), b.value(c)); @@ -327,7 +340,7 @@ public class Parser { private Expression parseAND() throws IOException, ParserException { Expression ac = parseShiftRight(); - while (isToken(Tokenizer.Token.AND)) { + while (nextIs(Tokenizer.Token.AND)) { Expression a = ac; Expression b = parseShiftRight(); ac = c -> Value.and(a.value(c), b.value(c)); @@ -337,7 +350,7 @@ public class Parser { private Expression parseShiftRight() throws IOException, ParserException { Expression ac = parseShiftLeft(); - while (isToken(Tokenizer.Token.SHIFTRIGHT)) { + while (nextIs(Tokenizer.Token.SHIFTRIGHT)) { Expression a = ac; Expression b = parseShiftLeft(); ac = c -> Value.toLong(a.value(c)) >> Value.toLong(b.value(c)); @@ -347,7 +360,7 @@ public class Parser { private Expression parseShiftLeft() throws IOException, ParserException { Expression ac = parseAdd(); - while (isToken(Tokenizer.Token.SHIFTLEFT)) { + while (nextIs(Tokenizer.Token.SHIFTLEFT)) { Expression a = ac; Expression b = parseAdd(); ac = c -> Value.toLong(a.value(c)) << Value.toLong(b.value(c)); @@ -357,7 +370,7 @@ public class Parser { private Expression parseAdd() throws IOException, ParserException { Expression ac = parseSub(); - while (isToken(Tokenizer.Token.ADD)) { + while (nextIs(Tokenizer.Token.ADD)) { Expression a = ac; Expression b = parseSub(); ac = c -> Value.add(a.value(c), b.value(c)); @@ -367,7 +380,7 @@ public class Parser { private Expression parseSub() throws IOException, ParserException { Expression ac = parseMul(); - while (isToken(Tokenizer.Token.SUB)) { + while (nextIs(Tokenizer.Token.SUB)) { Expression a = ac; Expression b = parseMul(); ac = c -> Value.toLong(a.value(c)) - Value.toLong(b.value(c)); @@ -377,7 +390,7 @@ public class Parser { private Expression parseMul() throws IOException, ParserException { Expression ac = parseDiv(); - while (isToken(Tokenizer.Token.MUL)) { + while (nextIs(Tokenizer.Token.MUL)) { Expression a = ac; Expression b = parseDiv(); ac = c -> Value.toLong(a.value(c)) * Value.toLong(b.value(c)); @@ -387,7 +400,7 @@ public class Parser { private Expression parseDiv() throws IOException, ParserException { Expression ac = parseMod(); - while (isToken(Tokenizer.Token.DIV)) { + while (nextIs(Tokenizer.Token.DIV)) { Expression a = ac; Expression b = parseMod(); ac = c -> Value.toLong(a.value(c)) / Value.toLong(b.value(c)); @@ -397,7 +410,7 @@ public class Parser { private Expression parseMod() throws IOException, ParserException { Expression ac = parseIdent(); - while (isToken(Tokenizer.Token.MOD)) { + while (nextIs(Tokenizer.Token.MOD)) { Expression a = ac; Expression b = parseIdent(); ac = c -> Value.toLong(a.value(c)) % Value.toLong(b.value(c)); @@ -439,10 +452,10 @@ public class Parser { private FirstClassFunction parseFunction() throws IOException, ParserException { expect(OPEN); ArrayList args = new ArrayList<>(); - if (!isToken(CLOSE)) { + if (!nextIs(CLOSE)) { expect(IDENT); args.add(tok.getIdent()); - while (!isToken(CLOSE)) { + while (!nextIs(CLOSE)) { expect(COMMA); expect(IDENT); args.add(tok.getIdent()); diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java b/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java index 09fff1665..5cfa18a1b 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java @@ -26,7 +26,7 @@ class Tokenizer { statementMap.put("if", Token.IF); statementMap.put("else", Token.ELSE); statementMap.put("for", Token.FOR); - statementMap.put("while", Token.FOR); + statementMap.put("while", Token.WHILE); statementMap.put("print", Token.PRINT); statementMap.put("printf", Token.PRINTF); statementMap.put("func", Token.FUNC); diff --git a/src/test/java/de/neemann/digital/core/element/ElementAttributesTest.java b/src/test/java/de/neemann/digital/core/element/ElementAttributesTest.java new file mode 100644 index 000000000..f72365b89 --- /dev/null +++ b/src/test/java/de/neemann/digital/core/element/ElementAttributesTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.core.element; + +import de.neemann.digital.core.memory.DataField; +import de.neemann.digital.hdl.hgs.Context; +import de.neemann.digital.hdl.hgs.HGSEvalException; +import de.neemann.digital.hdl.hgs.Parser; +import de.neemann.digital.hdl.hgs.ParserException; +import junit.framework.TestCase; + +import java.io.IOException; + +public class ElementAttributesTest extends TestCase { + + /** + * Ensures that the ElementAtributes is accessible from within the template engine + */ + public void testElementAttibutes() throws IOException, ParserException, HGSEvalException { + ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 5); + final Context c = new Context().setVar("elem", attr); + new Parser("bits=;").parse().execute(c); + assertEquals("bits=5;", c.toString()); + } + + /** + * Ensures that the DataField is accessible from within the template engine + */ + public void testDataField() throws IOException, ParserException, HGSEvalException { + DataField d = new DataField(5) + .setData(0, 1) + .setData(1, 7) + .setData(2, 4) + .setData(3, 8) + .setData(4, 2); + Context c= new Context().setVar("d", d); + new Parser("(0) print(\"-\"); print(d[i]);} ?>)").parse().execute(c); + assertEquals("(1-7-4-8-2)", c.toString()); + } + +} \ No newline at end of file diff --git a/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java b/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java index 7be7a464d..28652c812 100644 --- a/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java +++ b/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java @@ -5,9 +5,6 @@ */ package de.neemann.digital.hdl.hgs; -import de.neemann.digital.core.element.ElementAttributes; -import de.neemann.digital.core.element.Keys; -import de.neemann.digital.core.memory.DataField; import de.neemann.digital.hdl.hgs.function.FirstClassFunction; import de.neemann.digital.hdl.hgs.function.FuncAdapter; import de.neemann.digital.integration.FileScanner; @@ -150,6 +147,8 @@ public class ParserTest extends TestCase { assertEquals("Hello -4-5- World!", c.toString()); } + // for statement + public void testParseTemplateFor() throws IOException, ParserException, HGSEvalException { Context c = exec("Hello World!"); assertEquals("Hello 0123456789 World!", c.toString()); @@ -178,30 +177,11 @@ public class ParserTest extends TestCase { assertEquals("Hello (12)(24)(36) World!", c.toString()); } + // while statement - public void testParseTemplateElementAttibutes() throws IOException, ParserException, HGSEvalException { - ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 5); - Context c = exec("bits=;", new Context().setVar("elem", attr)); - assertEquals("bits=5;", c.toString()); - } - - public void testParseTemplateDataField() throws IOException, ParserException, HGSEvalException { - DataField d = new DataField(5) - .setData(0, 1) - .setData(1, 7) - .setData(2, 4) - .setData(3, 8) - .setData(4, 2); - Context c = exec("(0) print(\"-\"); print(d[i]);} ?>)", - new Context().setVar("d", d)); - assertEquals("(1-7-4-8-2)", c.toString()); - } - - public void testParseTemplateFormat() throws IOException, ParserException, HGSEvalException { - ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 17); - Context c = new Context().setVar("elem", attr); - exec("", c); - assertEquals("hex=11;", c.toString()); + public void testParseTemplateWhile() throws IOException, ParserException, HGSEvalException { + Context c = exec("Hello World!"); + assertEquals("Hello 0123456789 World!", c.toString()); } public void testParseTemplateArray() throws IOException, ParserException, HGSEvalException { @@ -249,13 +229,19 @@ public class ParserTest extends TestCase { assertEquals("4;", exec(t, new Context().setVar("m", 4)).toString()); } + public void testParseTemplateFormat() throws IOException, ParserException, HGSEvalException { + Context c = new Context().setVar("Bits", 17); + exec("", c); + assertEquals("hex=11;", c.toString()); + } + public void testComment() throws IOException, ParserException, HGSEvalException { Context c = exec(";"); assertEquals("false;", c.toString()); } public void testAddFunction() throws IOException, ParserException, HGSEvalException { - Statement s = new Parser("a : in ;").parse(); + Statement s = new Parser("a : in ;").parse(); Context funcs = new Context().setVar("type", new FuncAdapter(1) { @Override protected Object f(Object... args) throws HGSEvalException { @@ -268,10 +254,10 @@ public class ParserTest extends TestCase { }); assertEquals("a : in std_logic;", exec(s, new Context(funcs) - .setVar("elem", new ElementAttributes())).toString()); + .setVar("Bits", 1)).toString()); assertEquals("a : in std_logic_vector(5 downto 0);", exec(s, new Context(funcs) - .setVar("elem", new ElementAttributes().setBits(6))).toString()); + .setVar("Bits", 6)).toString()); } int flag = 0; @@ -339,10 +325,11 @@ public class ParserTest extends TestCase { } } + // checks the available VHDL templates public void testVHDLTemplates() throws Exception { final File path = new File(Resources.getRoot(), "../../main/resources/vhdl"); - int n=new FileScanner(f -> new Parser(new FileReader(f)).parse()).setSuffix(".tem").scan(path); - assertTrue(n>10); + int n = new FileScanner(f -> new Parser(new FileReader(f)).parse()).setSuffix(".tem").scan(path); + assertTrue(n > 10); } } \ No newline at end of file