/** * Copyright 2002-2017 Evgeny Gryaznov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.textmapper.tool.test.cases; import org.junit.Test; import org.textmapper.lapg.api.*; import org.textmapper.lapg.common.FileUtil; import org.textmapper.lapg.lex.LexerGenerator; import org.textmapper.tool.compiler.TMDataUtil; import org.textmapper.tool.compiler.TMGrammar; import org.textmapper.tool.gen.SyntaxUtil; import org.textmapper.lapg.lalr.Builder; import org.textmapper.tool.parser.TMTree.TextSource; import org.textmapper.lapg.test.TestStatus; import org.textmapper.templates.storage.ClassResourceLoader; import org.textmapper.templates.storage.ResourceRegistry; import org.textmapper.templates.types.TypesRegistry; import static org.junit.Assert.*; @SuppressWarnings({"deprecation"}) public class InputTest extends LapgTestCase { private TypesRegistry createDefaultTypesRegistry() { ResourceRegistry resources = new ResourceRegistry( new ClassResourceLoader(getClass().getClassLoader(), "org/textmapper/tool/test/cases/templates", "utf8"), new ClassResourceLoader(getClass().getClassLoader(), "org/textmapper/tool/templates", "utf8")); return new TypesRegistry(resources, (kind, message, anchors) -> fail(message)); } @Test public void testOptions() { TMGrammar g = SyntaxUtil.parseSyntax(new TextSource("syntax1options", FileUtil.getFileContents(openStream("syntax1options", TESTCONTAINER), FileUtil.DEFAULT_ENCODING), 1), new TestStatus(), createDefaultTypesRegistry()); assertNotNull(g); assertEquals(0, g.getGrammar().getEoi().getIndex()); Object container = g.getOptions().get("container"); assertNotNull(container); } @Test public void testCheckSimple1() { TMGrammar g = SyntaxUtil.parseSyntax(new TextSource("syntax1", FileUtil.getFileContents(openStream("syntax1", TESTCONTAINER), FileUtil.DEFAULT_ENCODING), 1), new TestStatus(), createDefaultTypesRegistry()); assertNotNull(g); assertEquals(0, g.getGrammar().getEoi().getIndex()); Symbol[] syms = g.getGrammar().getSymbols(); assertEquals(7, syms.length); assertEquals(Symbol.EOI.text(), syms[0].getNameText()); assertEquals("identifier", syms[1].getNameText()); assertEquals("Licon", syms[2].getNameText()); assertEquals("_skip", syms[3].getNameText()); // TODO do not // collect skip // symbols assertEquals("input", syms[4].getNameText()); assertEquals("list", syms[5].getNameText()); assertEquals("list_item", syms[6].getNameText()); Rule[] rules = g.getGrammar().getRules(); assertEquals(5, rules.length); assertEquals("input", rules[0].getLeft().getNameText()); assertEquals("list", rules[0].getRight()[0].getTarget().getNameText()); assertEquals(1, rules[0].getRight().length); LexerRule[] lexerRules = g.getGrammar().getLexerRules(); assertEquals(3, lexerRules.length); assertEquals("@?[a-zA-Z_][A-Za-z_0-9]*", lexerRules[0].getRegexp().toString()); assertEquals("([1-9][0-9]*|0[0-7]*|0[xX][0-9a-fA-F]+)([uU](l|L|ll|LL)?|(l|L|ll|LL)[uU]?)?", lexerRules[1] .getRegexp().toString()); assertEquals("[\\t\\r\\n ]+", lexerRules[2].getRegexp().toString()); assertEquals("{ continue; }", TMDataUtil.getCode(lexerRules[2]).getText()); } @Test public void testCheckSimple2() { TMGrammar g = SyntaxUtil.parseSyntax(new TextSource("syntax2", FileUtil.getFileContents(openStream("syntax2", TESTCONTAINER), FileUtil.DEFAULT_ENCODING), 1), new TestStatus(), createDefaultTypesRegistry()); assertNotNull(g); assertEquals(0, g.getGrammar().getEoi().getIndex()); Symbol[] syms = g.getGrammar().getSymbols(); assertEquals(11, syms.length); assertEquals(Symbol.EOI.text(), syms[0].getNameText()); assertEquals("a", syms[1].getNameText()); assertEquals("b", syms[2].getNameText()); assertEquals("'('", syms[3].getNameText()); assertEquals("')'", syms[4].getNameText()); assertEquals("input", syms[5].getNameText()); assertEquals("list", syms[6].getNameText()); assertEquals("item", syms[7].getNameText()); assertEquals("item3", syms[8].getNameText()); assertEquals("subitem", syms[9].getNameText()); assertEquals("listopt", syms[10].getNameText()); assertEquals(13, g.getGrammar().getRules().length); assertEquals("{ ${for a in b}..!..$$ }", TMDataUtil.getCode(g.getGrammar().getRules()[5]).getText()); assertEquals(1, g.getGrammar().getRules()[9].getRight().length); } @Test public void testTextSource() { TextSource source = new TextSource("file", "aa\nbb\n\nc", 7); int[] expected = new int[]{7, 7, 7, 8, 8, 8, 9, 10}; for (int i = 0; i < expected.length; i++) { assertEquals("offset #" + i, expected[i], source.lineForOffset(i)); } } @Test public void testClassLexemes() { TestStatus notifier = new TestStatus("", "syntax_lexemes,25: regex matches two classes `identifier' and `identifierX', using first\n" + "syntax_lexemes,28: soft lexeme rule `L0choice' doesn't match any class rule\n" + "syntax_lexemes,31: soft lexeme rule `int' should have a constant regexp\n" + "syntax_lexemes,39: redeclaration of soft class for `abcde': found icon instead of identifier\n" + "syntax_lexemes,42: redeclaration of soft terminal: ssss\n" + "syntax_lexemes,45: soft lexeme rule `wact' cannot have a semantic action\n" + "syntax_lexemes,48: soft terminal `wtype' overrides base type: expected `<no type>', found `int'\n" + "syntax_lexemes,55: soft terminal `comma' overrides base type: expected `char', found `Character'\n" ); TMGrammar g = SyntaxUtil.parseSyntax(new TextSource("syntax_lexemes", FileUtil.getFileContents(openStream("syntax_lexemes", TESTCONTAINER), FileUtil.DEFAULT_ENCODING), 1), notifier, createDefaultTypesRegistry()); notifier.assertDone(); assertNull(g); } @Test public void testNamedPatterns() { TestStatus notifier = new TestStatus("", "syntax_patterns,10: regexp is incomplete\n" + "syntax_patterns,19: redeclaration of named pattern `WORD', ignored\n" ); TMGrammar g = SyntaxUtil.parseSyntax(new TextSource("syntax_patterns", FileUtil.getFileContents(openStream("syntax_patterns", TESTCONTAINER), FileUtil.DEFAULT_ENCODING), 1), notifier, createDefaultTypesRegistry()); notifier.assertDone(); assertNull(g); } @Test public void testCheckCSharpGrammar() { TestStatus ts = new TestStatus(); TMGrammar g = SyntaxUtil.parseSyntax(new TextSource("input", FileUtil.getFileContents(openStream("syntax_cs", TESTCONTAINER), FileUtil.DEFAULT_ENCODING), 1), ts, createDefaultTypesRegistry()); assertNotNull(g); ts.reset("input,8: symbol `error` is useless\n" + "input,49: symbol `Lfixed` is useless\n" + "input,81: symbol `Lstackalloc` is useless\n" + "input,154: symbol `comment` is useless\n" + "input,160: symbol `'/*'` is useless\n" + "input,162: symbol `anysym1` is useless\n" + "input,164: symbol `'*/'` is useless\n", "input,486: input: using_directivesopt attributesopt modifiersopt Lclass ID class_baseopt '{' attributesopt modifiersopt operator_declarator '{' Lif '(' expression ')' embedded_statement\n" + "shift/reduce conflict (next: Lelse)\n" + " embedded_statement : Lif '(' expression ')' embedded_statement\n" + "\n" + "conflicts: 1 shift/reduce and 0 reduce/reduce\n"); LexerGenerator.generate(g.getGrammar().getLexerStates(), g.getGrammar().getLexerRules(), g.getGrammar().getPatterns(), ts); Builder.compile(g.getGrammar(), ts); ts.assertDone(); assertTrue(g.getTemplates().getText().startsWith("//#define DEBUG_syntax")); } @Test public void testCheckConflictsHandling() { TestStatus ts = new TestStatus(); TMGrammar g = SyntaxUtil.parseSyntax(new TextSource("syntax_conflict1", FileUtil.getFileContents(openStream("syntax_conflict1", TESTCONTAINER), FileUtil.DEFAULT_ENCODING), 1), ts, createDefaultTypesRegistry()); assertNotNull(g); ts.reset( "", "syntax_conflict1,22: input: Licon\n" + "reduce/reduce conflict (next: fix1, fix2, fix3)\n" + " input1 : Licon\n" + " list_item : Licon\n" + "\n" + "conflicts: 0 shift/reduce and 1 reduce/reduce\n"); LexerGenerator.generate(g.getGrammar().getLexerStates(), g.getGrammar().getLexerRules(), g.getGrammar().getPatterns(), ts); Builder.compile(g.getGrammar(), ts); ts.assertDone(); } @Test public void testCheckConflictsResolving() { final boolean[] isDebug = new boolean[]{false}; TestStatus ts = new TestStatus("", "") { @Override public void handle(int kind, String text) { if (kind == KIND_DEBUG && isDebug[0]) { return; // ignore } super.handle(kind, text); } @Override public boolean isAnalysisMode() { return isDebug[0]; } }; TMGrammar g = SyntaxUtil.parseSyntax(new TextSource("syntax_conflict2resolved", FileUtil.getFileContents(openStream("syntax_conflict2resolved", TESTCONTAINER), FileUtil.DEFAULT_ENCODING), 1), ts, createDefaultTypesRegistry()); assertNotNull(g); ts.reset("", ""); LexerGenerator.generate(g.getGrammar().getLexerStates(), g.getGrammar().getLexerRules(), g.getGrammar().getPatterns(), ts); Builder.compile(g.getGrammar(), ts); ts.assertDone(); isDebug[0] = true; ts.reset("syntax_conflict2resolved,42: input: Lid '=' expr '*' expr\n" + "resolved as reduce conflict (next: '*', '+', '-', '/')\n" + " expr : expr '*' expr\n" + "\n" + "syntax_conflict2resolved,44: input: Lid '=' expr '+' expr\n" + "resolved as shift conflict (next: '*', '/')\n" + " expr : expr '+' expr\n" + "\n" + "syntax_conflict2resolved,44: input: Lid '=' expr '+' expr\n" + "resolved as reduce conflict (next: '+', '-')\n" + " expr : expr '+' expr\n" + "\n" + "syntax_conflict2resolved,45: input: Lid '=' expr '-' expr\n" + "resolved as shift conflict (next: '*', '/')\n" + " expr : expr '-' expr\n" + "\n" + "syntax_conflict2resolved,45: input: Lid '=' expr '-' expr\n" + "resolved as reduce conflict (next: '+', '-')\n" + " expr : expr '-' expr\n" + "\n" + "syntax_conflict2resolved,43: input: Lid '=' expr '/' expr\n" + "resolved as reduce conflict (next: '*', '+', '-', '/')\n" + " expr : expr '/' expr\n" + "\n", ""); LexerGenerator.generate(g.getGrammar().getLexerStates(), g.getGrammar().getLexerRules(), g.getGrammar().getPatterns(), ts); Builder.compile(g.getGrammar(), ts); ts.assertDone(); } }