Seite 1 von 1

[Pyparsing] Wie am besten Aktionen hinzufügen

Verfasst: Sonntag 6. April 2008, 15:52
von sma
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