[Pyparsing] Wie am besten Aktionen hinzufügen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Eine PEG (wie Pyparsing sie benutzt) hat den Vorteil, recht lesbar zu sein - jedenfalls solange wie man keine Aktionen hinzufügt. Doch wenn man das will, wie geht man da am besten vor?

Hier ist ein Beispiel:

Code: Alles auswählen

symbol = identifier | operator | OneOrMore(keyword)
symbol_constant = "#" + (symbol | string)
array_constant = Forward()
literal = number | string | "nil" |  "true" | "false" | symbol_constant | array_constant
array = Forward()
array << "(" + ZeroOrMore(literal | symbol | array) + ")"
array_constant << "#" + array
Bei obigem Beispiel möchte ich gerne, dass `literal` ein passendes Objekt erzeugt. Ich muss da insbesondere an jede Alternative eine eigene Aktion hängen, was das ganze unlesbar macht.

Also nachträglich sowas machen?

Code: Alles auswählen

literal.exprs[0].setParseAction(...)
literal.exprs[1].setParseAction(...)
literal.exprs[2].setParseAction(...)
array.setParseAction(...)
Oder in den sauren Apfel beißen und die Lesbarkeit opfern?

Code: Alles auswählen

symbol = identifier | operator | OneOrMore(keyword).setParseAction(lambda r: "".join(r))
symbol_constant = Suppress("#") + (symbol | string).setParseAction(lambda r: rt.Symbol(r[0]))
array_constant = Forward()
literal = (
	number.setParseAction(lambda r: rt.Integer(int(r[0]))) | 
	string.setParseAction(lambda r: rt.String(r[0][1:-1].replace("''", "'"))) | 
	Literal("nil").setParseAction(lambda r: rt.System.nil) |
	Literal("true").setParseAction(lambda r: rt.System.true) |
	Literal("false").setParseAction(lambda r: rt.System.false) |
	symbol_constant | 
	array_constant
)
array = Forward()
array << Suppress("(") + ZeroOrMore(
	literal | 
	symbol.setParseAction(lambda r: rt.Symbol(r[0])) | 
	array
).setParseAction(lambda r: rt.Array(tuple(r))) + Suppress(")")
array_constant << Suppress("#") + array
Oder doch eine externe DSL benutzen?

Code: Alles auswählen

symbol:
	identifier
	operator
	keyword+     	=> "".join($0)
	
symbol_constant:
	"#" symbol		=> rt.Symbol($0)
	"#" string		=> rt.Symbol($0)
	
array_constant:
	"#" array
	
array:
	"(" array_contents* ")" => rt.Array($0)
	
array_contents:
	literal
	symbol			=> rt.Symbol($0)
	array

literal:
	number 			=> rt.Integer(int($0))
	string 			=> rt.String($0)
	"nil"  			=> rt.System.nil
	"true" 			=> rt.System.true
	"false" 		=> rt.System.false
	symbol_constant
	array_constant
Jede Regel wird durch einen ":" abgeschlossen, dem dann eingerückt die Alternativen folgen. Hinter "=>" steht die Aktion als Python-Ausdruck, wobei "$x" auf das x-te Ergebnis der Unterregeln verweist. Token in "" werden automatisch ignoriert und "=> $0" ist der Standard.

Gibt es noch Alternativen, Zustimmung oder Ablehnung?

Stefan
Antworten