I try to create simple DSL with Python SLY . But, I can't get the result as I expected because the parser can't read it properly. So here the code :
Lexer
from sly import Lexer
class ConfigLexer(Lexer):
tokens = { ANIMALS, BLOOD, SKIN, BREATHE, ANIMAL_NAME, VALUE, ASSIGN }
ignore = " \t\r"
ignore_newline = r'\n+'
ANIMALS = "ANIMALS"
BLOOD = "BLOOD"
SKIN = "SKIN"
BREATHE = "BREATHE"
ANIMAL_NAME = r'\{[a-zA-Z_][a-zA-Z0-9_]*\}'
VALUE = r'[a-zA-Z_][a-zA-Z0-9_,.: ]*'
ASSIGN = r'\='
Parser
from sly import Parser
class ConfigParser(Parser):
tokens = ConfigLexer.tokens
def __init__(self):
self.config = dict()
self.dict_attribute = dict()
self.animal_name = ""
@_("ANIMALS animaldetails")
def animals(self, p):
pass
@_("ANIMAL_NAME animalnamedetails")
def animaldetails(self, p):
self.animal_name = p.ANIMAL_NAME.replace("{", "").replace("}","")
if self.animal_name not in self.config:
self.config[self.animal_name] = self.dict_attribute
@_("BLOOD ASSIGN VALUE")
def animalnamedetails(self, p):
if p.BLOOD not in self.dict_attribute:
self.dict_attribute[p.BLOOD] = p.VALUE
@_("SKIN ASSIGN VALUE")
def animalnamedetails(self, p):
if p.SKIN not in self.dict_attribute:
self.dict_attribute[p.SKIN] = p.VALUE
@_("BREATHE ASSIGN VALUE")
def animalnamedetails(self, p):
if p.BREATHE not in self.dict_attribute:
self.dict_attribute[p.BREATHE] = p.VALUE
def get_config(self):
return self.config
but when I run it.
import json
import ConfigLexer
import ConfigParser
if __name__ == '__main__':
lexer = ConfigLexer()
parser = ConfigParser()
long_string = """ANIMALS
{MAMMALS}
BLOOD = WARM
SKIN = FUR
BREATHE = LUNGS
{FISH}
BLOOD = COLD
SKIN = SCALY
BREATHE = GILLS"""
result = parser.parse(lexer.tokenize(long_string))
cfg = parser.get_config()
data_json = json.dumps(cfg, indent=3)
print(data_json)
as I expected, the result would be like this.
data_json = {
'MAMMALS': {'BLOOD': 'WAMR': 'SKIN': 'FUR OR HAIR', 'BREATHE': 'LUNGS'},
'FISH': {'BLOOD': 'COLD', 'SKIN': 'SCALY', 'BREATHE': 'GILLS'}
}
but I only get something like this.
data_json = {
'MAMMALS': {
'BLOOD': 'WARM'
}
}
result of executing :
sly: Syntax error at line 1, token=SKIN
{
"MAMMALS": {
"BLOOD": "WARM"
}
}
I guess I have to edit the Parser, but I can't think how, and would appreciate any pointers you can give me.

You have non-terminals named
animals,animaldetails, andanimalnameddetails, in plural, which would normally lead one to expect that the grammar for each of them would allow a sequence of things. But they don't. Each of these categories parses a single thing. You've implemented the singular, and although it's named in plural, there's no repetition.That this was not your intent is evident from your example, which does have multiple sections and multiple attributes in each section. But since the grammar only describes one attribute and value, the second one is a syntax error.
Traditionally, grammars will implement sequences with pairs of non-terminals; a singular non-terminal which describes a single thing, and a plural non-terminal which describes how lists are formed (simple concatenation, or separated by punctuation). So you might have:
You probably should also look fora description of how to manage semantic values. Storing intermediate results in class members, as you do, works only when the grammar doesn't allow nesting, which is relatively unusual. It's a technique which will almost always get you into trouble. The semantic actions of each production should manage these values: