Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a DSL in PHP

Tags:

php

dsl

I have an idea. I want to give our client the ability to specify pricing based on a number of variables by writing some simple code like this:

if customer.zip is "37208"
   return 39.99
else
   return 59.99

And in my code, I'd do something like this:

try {
   $variables = array('customer' => array('zip' => '63901'));
   $code = DSL::parse(DSL::tokenize($userCode))
   $returnValue = DSL::run($code, $variables);
} catch (SyntaxErrorException $e) {
   ...
}

I guess what I'm wanting is to create a simple DSL in PHP that allows our customer to have a great deal of flexibility in setting pricing without having to have us code each and every case.

Here's the basic idea:

  1. I would provide an array of variables and the code that the customer wrote.
  2. The parser would evaluate the code that the user wrote using the variables provided and return to me the value that our customer returned. It would throw exceptions for any syntax errors, etc.
  3. I would then use the returned value in the normal logic of the application.

So do you know of any resources or frameworks for building a simple DSL in PHP? Any ideas where to begin?

Thanks!

like image 851
Jesse Bunch Avatar asked Dec 18 '12 20:12

Jesse Bunch


3 Answers

The standard approach to building a DSL parser is to employ a parser generator aka a compiler-compiler to do the heavy lifting. This allows the developer to express the DSL in an abstract BNF-ish syntax, and not have to get into the nitty gritty of parsing and lexing.

Examples include Yacc in C, Regexp::Grammars in Perl, and ANTLR, which targets Java and several other languages, etc. The PHP option appears to be PHP-PEG.

like image 190
djsadinoff Avatar answered Oct 26 '22 16:10

djsadinoff


Technical limitations aside, you might want to really think twice about giving this kind of programming power to (I presume) non-programmers. They will probably mess up in completely unpredictable ways and you'll be the one having to clean up the mess. At least guard it with lots of tests. And possibly legalese as well.

But you asked a question, so I'll try to answer that. There is a distinction to be made between internal style DSL's (What most people mean when they use the word DSL) and then external style DSL's (Which is more like a mini language). Ruby is famous for having a syntax that lends it self well to internal style DSL. PHP on the other hand, is quite bad in that regard.

That said, you can still do some stuff in PHP - The simplest is perhaps to just write up a library of functions and then have your customers write code in plain PHP, using that library. You would have to audit the code of course, but it would give all the benefits of using an existing runtime.

If that's not fancy enough, you will have to dig in to the heavy stuff. First you need a parser. If you know how, they can be hand written fairly easily, but unless you were forced to write one in school or you have a strange hobby of writing that kind of stuff just for fun (I do), it's probably going to take you a bit of work. The basic components of a parser is a tokenizer and some kind of automata (state machine) that arranges the tokens into a tree-structure (an AST).

Once you have your parsed structure, you need to evaluate it. Since this is a DSL, the number of features are limited and performance is probably not your biggest concern, you could write some object oriented code around the AST and leave it at that. Otherwise you have options like writing some sort of interpreter or cross-compile it into another format (PHP would be an obvious choice).

The tricky part all way through this is mostly in handling edge cases, such as syntax errors and report something meaningful back to the user. Again, just giving then access to a subset of PHP, will give you that for free, so consider that first.

like image 31
troelskn Avatar answered Oct 26 '22 17:10

troelskn


If anyone else is looking for another option - consider using Twig for creating the DSL/parsing (http://twig.sensiolabs.org/) which is integrated to the Pico CMS (http://pico.dev7studios.com/#).

like image 35
1car69 Avatar answered Oct 26 '22 16:10

1car69