CodeSOD: A Dumbain Specific Language
I've had to write a few domain-specific-languages in the past. As per Remy's Law of Requirements Gathering, it's been mostly because the users needed an Excel-like formula language. The danger of DSLs, of course, is that they're often YAGNI in the extreme, or at least a sign that you don't really understand your problem.
XML, coupled with schemas, is a tool for building data-focused DSLs. If you have some complex structure, you can convert each of its features into an XML attribute. For example, if you had a grammar that looked something like this:
The Source specification obeys the following syntaxsource = ( Feature1+Feature2+... ":" ) ? stepsFeature1 = "local" | "global"Feature2 ="real" | "virtual" | "ComponentType.all"Feature3 ="self" | "ancestors" | "descendants" | "Hierarchy.all"Feature4 = "first" | "last" | "DayAllocation.all"If features are specified, the order of features as given above has strictly to be followed.steps = oneOrMoreNameSteps | zeroOrMoreNameSteps | componentStepsoneOrMoreNameSteps = nameStep ( "." nameStep ) *zeroOrMoreNameSteps = ( nameStep "." ) *nameStep = "#" namename is a string of characters from "A"-"Z", "a"-"z", "0"-"9", "-" and "_". No umlauts allowed, one character is minimum.componentSteps is a list of valid values, see below.Valid 'componentSteps' are:- GlobalValue- Product- Product.Brand- Product.Accommodation- Product.Accommodation.SellingAccom- Product.Accommodation.SellingAccom.Board- Product.Accommodation.SellingAccom.Unit- Product.Accommodation.SellingAccom.Unit.SellingUnit- Product.OnewayFlight- Product.OnewayFlight.BookingClass- Product.ReturnFlight- Product.ReturnFlight.BookingClass- Product.ReturnFlight.Inbound- Product.ReturnFlight.Outbound- Product.Addon- Product.Addon.Service- Product.Addon.ServiceFeatureIn addition to that all subsequent steps from the paths above are permitted, that is 'Board', 'Accommodation.SellingAccom' or 'SellingAccom.Unit.SellingUnit'.'Accommodation.Unit' in the contrary is not permitted, as here some intermediate steps are missing.
You could turn that grammar into an XML document by converting syntax elements to attributes and elements. You could do that, but Stella's predecessor did not do that. That of course, would have been work, and they may have had to put some thought on how to relate their homebrew grammar to XSD rules, so instead they created an XML schema rule for SourceAttributeType that verifies that the data in the field is valid according to the grammar" using regular expressions. 1,310 characters of regular expressions.
<xs:simpleType> <xs:restriction base="xs:string"> <xs:pattern value="(((Scope.)?(global|local|current)\+?)?((((ComponentType.)?(real|virtual))|ComponentType.all)\+?)?((((Hierarchy.)?(self|ancestors|descendants))|Hierarchy.all)\+?)?((((DayAllocation.)?(first|last))|DayAllocation.all)\+?)?:)?(#[A-Za-z0-9\-_]+(\.(#[A-Za-z0-9\-_]+))*|(#[A-Za-z0-9\-_]+\.)*(ThisComponent|GlobalValue|Product|Product\.Brand|Product\.Accommodation|Product\.Accommodation\.SellingAccom|Product\.Accommodation\.SellingAccom\.Board|Product\.Accommodation\.SellingAccom\.Unit|Product\.Accommodation\.SellingAccom\.Unit\.SellingUnit|Product\.OnewayFlight|Product\.OnewayFlight\.BookingClass|Product\.ReturnFlight|Product\.ReturnFlight\.BookingClass|Product\.ReturnFlight\.Inbound|Product\.ReturnFlight\.Outbound|Product\.Addon|Product\.Addon\.Service|Product\.Addon\.ServiceFeature|Brand|Accommodation|Accommodation\.SellingAccom|Accommodation\.SellingAccom\.Board|Accommodation\.SellingAccom\.Unit|Accommodation\.SellingAccom\.Unit\.SellingUnit|OnewayFlight|OnewayFlight\.BookingClass|ReturnFlight|ReturnFlight\.BookingClass|ReturnFlight\.Inbound|ReturnFlight\.Outbound|Addon|Addon\.Service|Addon\.ServiceFeature|SellingAccom|SellingAccom\.Board|SellingAccom\.Unit|SellingAccom\.Unit\.SellingUnit|BookingClass|Inbound|Outbound|Service|ServiceFeature|Board|Unit|Unit\.SellingUnit|SellingUnit))"/> </xs:restriction></xs:simpleType></xs:union>
There's a bug in that regex that Stella needed to fix. As she put it: "Every time you evaluate it a few little kitties die because you shouldn't use kitties to polish your car. I'm so, so sorry, little kitties""
The full, unexcerpted code is below, so" at least it has documentation. In two languages!
<xs:simpleType name="SourceAttributeType"> <xs:annotation> <xs:documentation xml:lang="de"> Die Source Angabe folgt folgender Syntax source = ( Eigenschaft1+Eigenschaft2+... ":" ) ? steps Eigenschaft1 = "local" | "global" Eigenschaft2 ="real" | "virtual" | "ComponentType.all" Eigenschaft3 ="self" | "ancestors" | "descendants" | "Hierarchy.all" Eigenschaft4 = "first" | "last" | "DayAllocation.all" Falls Eigenschaften angegeben werden muss zwingend die oben angegebene Reihenfolge der Eigenschaften eingehalten werden. steps = oneOrMoreNameSteps | zeroOrMoreNameSteps | componentSteps oneOrMoreNameSteps = nameStep ( "." nameStep ) * zeroOrMoreNameSteps = ( nameStep "." ) * nameStep = "#" name name ist eine Folge von Zeichen aus der Menge "A"-"Z", "a"-"z", "0"-"9", "-" und "_". Keine Umlaute. Mindestens ein Zeichen componentSteps ist eine Liste gi1/4ltiger Werte, siehe im folgenden Gi1/4ltige 'componentSteps' sind zunichst: - GlobalValue - Product - Product.Brand - Product.Accommodation - Product.Accommodation.SellingAccom - Product.Accommodation.SellingAccom.Board - Product.Accommodation.SellingAccom.Unit - Product.Accommodation.SellingAccom.Unit.SellingUnit - Product.OnewayFlight - Product.OnewayFlight.BookingClass - Product.ReturnFlight - Product.ReturnFlight.BookingClass - Product.ReturnFlight.Inbound - Product.ReturnFlight.Outbound - Product.Addon - Product.Addon.Service - Product.Addon.ServiceFeature Desweiteren sind alle Unterschrittfolgen aus obigen Pfaden erlaubt, also 'Board', 'Accommodation.SellingAccom' oder 'SellingAccom.Unit.SellingUnit'. 'Accommodation.Unit' hingegen ist nicht erlaubt, da in diesem Fall einige Zwischenschritte fehlen. </xs:documentation> <xs:documentation xml:lang="en"> The Source specification obeys the following syntax source = ( Feature1+Feature2+... ":" ) ? steps Feature1 = "local" | "global" Feature2 ="real" | "virtual" | "ComponentType.all" Feature3 ="self" | "ancestors" | "descendants" | "Hierarchy.all" Feature4 = "first" | "last" | "DayAllocation.all" If features are specified, the order of features as given above has strictly to be followed. steps = oneOrMoreNameSteps | zeroOrMoreNameSteps | componentSteps oneOrMoreNameSteps = nameStep ( "." nameStep ) * zeroOrMoreNameSteps = ( nameStep "." ) * nameStep = "#" name name is a string of characters from "A"-"Z", "a"-"z", "0"-"9", "-" and "_". No umlauts allowed, one character is minimum. componentSteps is a list of valid values, see below. Valid 'componentSteps' are: - GlobalValue - Product - Product.Brand - Product.Accommodation - Product.Accommodation.SellingAccom - Product.Accommodation.SellingAccom.Board - Product.Accommodation.SellingAccom.Unit - Product.Accommodation.SellingAccom.Unit.SellingUnit - Product.OnewayFlight - Product.OnewayFlight.BookingClass - Product.ReturnFlight - Product.ReturnFlight.BookingClass - Product.ReturnFlight.Inbound - Product.ReturnFlight.Outbound - Product.Addon - Product.Addon.Service - Product.Addon.ServiceFeature In addition to that all subsequent steps from the paths above are permitted, that is 'Board', 'Accommodation.SellingAccom' or 'SellingAccom.Unit.SellingUnit'. 'Accommodation.Unit' in the contrary is not permitted, as here some intermediate steps are missing. </xs:documentation> </xs:annotation> <xs:union> <xs:simpleType> <xs:restriction base="xs:string"> <xs:pattern value="(((Scope.)?(global|local|current)\+?)?((((ComponentType.)?(real|virtual))|ComponentType.all)\+?)?((((Hierarchy.)?(self|ancestors|descendants))|Hierarchy.all)\+?)?((((DayAllocation.)?(first|last))|DayAllocation.all)\+?)?:)?(#[A-Za-z0-9\-_]+(\.(#[A-Za-z0-9\-_]+))*|(#[A-Za-z0-9\-_]+\.)*(ThisComponent|GlobalValue|Product|Product\.Brand|Product\.Accommodation|Product\.Accommodation\.SellingAccom|Product\.Accommodation\.SellingAccom\.Board|Product\.Accommodation\.SellingAccom\.Unit|Product\.Accommodation\.SellingAccom\.Unit\.SellingUnit|Product\.OnewayFlight|Product\.OnewayFlight\.BookingClass|Product\.ReturnFlight|Product\.ReturnFlight\.BookingClass|Product\.ReturnFlight\.Inbound|Product\.ReturnFlight\.Outbound|Product\.Addon|Product\.Addon\.Service|Product\.Addon\.ServiceFeature|Brand|Accommodation|Accommodation\.SellingAccom|Accommodation\.SellingAccom\.Board|Accommodation\.SellingAccom\.Unit|Accommodation\.SellingAccom\.Unit\.SellingUnit|OnewayFlight|OnewayFlight\.BookingClass|ReturnFlight|ReturnFlight\.BookingClass|ReturnFlight\.Inbound|ReturnFlight\.Outbound|Addon|Addon\.Service|Addon\.ServiceFeature|SellingAccom|SellingAccom\.Board|SellingAccom\.Unit|SellingAccom\.Unit\.SellingUnit|BookingClass|Inbound|Outbound|Service|ServiceFeature|Board|Unit|Unit\.SellingUnit|SellingUnit))"/> </xs:restriction> </xs:simpleType> </xs:union></xs:simpleType>[Advertisement] Release!is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!