From e31fdee08ae5debcddff74c6f2340592314e7913 Mon Sep 17 00:00:00 2001 From: Tomas Gavenciak Date: Sat, 10 Jul 2010 13:05:23 +0200 Subject: [PATCH] Implemented varname as a shorthand for "{varname}" Fixed: variable parsing at end of file Updated grammar and tests, new test --- t/moe/confparser.py | 10 ++++++---- t/moe/conftest.py | 15 ++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/t/moe/confparser.py b/t/moe/confparser.py index 3178340..0ac7bb2 100644 --- a/t/moe/confparser.py +++ b/t/moe/confparser.py @@ -39,7 +39,7 @@ NOTE: ';' or '\n' is currently required even after CONDITION and SUBTREE block TODO: change to OPERATION only NOTE: Formula may contain additional/extra parentheses -EXPRESSION = '"' ( ECHAR | '{' VARNAME '}' )* '"' | re"'[^'\\n]*'" +EXPRESSION = '"' ( ECHAR | '{' VARNAME '}' )* '"' | re"'[^'\\n]*'" | VARNAME ECHAR = re('([^\\{}]|\\\\|\\{|\\}|\\n)*') VARNAME = re('[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)*') """ @@ -278,7 +278,7 @@ class ConfigParser(object): def p_VARNAME(self): self.dbg() # Debug vnl = [] - while self.peek().isalnum() or self.peek() in u'-_.': + while self.preread(1) and (self.peek().isalnum() or self.peek() in u'-_.'): vnl.append(self.next()) vn = u''.join(vnl) if not conf.re_VARNAME.match(vn): @@ -287,9 +287,11 @@ class ConfigParser(object): def p_EXPRESSION(self): self.dbg() # Debug + if self.peek() not in '\'"': + # Expect a variable name + varname = self.p_VARNAME() + return conf.ConfigExpression((self.tree.lookup(varname),), varname) op = self.next() - if op not in '\'"': - self.syntax_error('Invalid start of expression') # Parse literal expression if op == u'\'': exl = [] diff --git a/t/moe/conftest.py b/t/moe/conftest.py index b065d04..85cf7df 100644 --- a/t/moe/conftest.py +++ b/t/moe/conftest.py @@ -25,15 +25,16 @@ class TestConfig(unittest.TestCase): return s.var(varname, create=False).value() def eqparse(s, string, *args, **kwargs): + "Parse expression `s`, return parts that should be equal to anoter run of `eqparse(s)`." return [(i[0], i[1].operation) for i in s.parse(string, *args, **kwargs)] class TestParser(TestConfig): - s1 = r"""a="1";z{b='2';w{S.Tr_an-g.e='""'}};c.d='\n';e="{a}{b}";e+='{c.d}';a+="\"\n\{\}";f+='Z{a.b}'""" + s1 = r"""a="1";z{b='2';w{S.Tr_an-g.e='"'}};c.d='\n';e="{a}{b}";e+='{c.d}';a+="\"\n\{\}";f+='Z{a.b}';e+=c.d;e+=c;e+=a""" s2 = '\t\n \n ' + s1.replace('=', '= \n ').replace(';', '\t \n\t \n ').replace('+=',' \n += ') + '\n\n ' def test_noWS(s): - assert len(s.parse(s.s1)) == 8 + assert len(s.parse(s.s1)) == 11 def test_noWS_COMMENT(s): assert s.eqparse(s.s1+'#COMMENT') == s.eqparse(s.s1+'#') == s.eqparse(s.s1+'#\n') == s.eqparse(s.s1+'\n#') @@ -53,6 +54,8 @@ class TestParser(TestConfig): s.assertRaises(ConfigSyntaxError, s.parse, "a='\"") s.assertRaises(ConfigSyntaxError, s.parse, 'a="{a@b}"') s.assertRaises(ConfigSyntaxError, s.parse, 'a="A{A"') + s.assertRaises(ConfigSyntaxError, s.parse, 'a=b"42"') + s.assertRaises(ConfigSyntaxError, s.parse, 'a=b.c.d.') def test_error_location(s): try: s.parse('\t \n \n { \n \n ') @@ -72,6 +75,7 @@ class TestParser(TestConfig): s.assertRaises(ConfigSyntaxError, s.parse, "if '{a}'=='{b}' and ''!='' {}") s.parse('if ((#C\n (\n (not not not""!="")\n#C\n)\t ) ) {}') s.parse('if (""=="" and not (not not ""!="" or ""=="")){}') + s.parse('if ((a=="{b}" and a.b.c!=c.b.a)or x!=y){}') s.parse('if(""==""){a{if(""==""){if(""==""){b{if(""==""){if(""==""){}}}}}}}') s.assertRaises(ConfigSyntaxError, s.parse, "if notnot'{a}'=='{b}' {}") s.assertRaises(ConfigSyntaxError, s.parse, "if ('{a}'=='{b}' not and ''!='') {}") @@ -96,6 +100,11 @@ class TestConfigEval(TestConfig): def test_escape_chars(s): s.parse(r"""a='{a}\\\\#\n'; b="{a}'\"\{\}"; c='\'; c+="\{{b}\}";""") assert s.val('c') == r"""\{{a}\\\\#\n'"{}}""" + + def test_expressions(s): + s.parse('A="4"; B.B=\'2\'; B.A=A; B.C="{B.A}{B.B}"; if B.C=="42" {D=C}; C="OK"') + assert s.val('D') == 'OK' + ts = 'a="A:"; if "{c1}"=="1" {a+="C1"; b="B"; if ("{c2a}"=="1" or not "{c2b}"=="1") { a+="C2"; '\ 'if ("{c3a}"=="1" and "{c3b}"=="1") { a+="C3" }}}' @@ -200,7 +209,7 @@ class TestConfigEval(TestConfig): -# TODO: Test conditions +# TODO: Test priority # TODO: Fail on 1st April # TODO: Somehow add log.debug('Maximum encountered depth: %d', conf.debug_maxdepth) -- 2.39.2