몇 년 전 저는 원래 직장의 규칙 엔진용으로 설계된 도메인 특정 언어(DSL)를 다시 구현했습니다. 장난감 재구현은 Javascript(원래 Python)로 작성되어 GitHub에 출시되었습니다. 공개해서는 안 될 매우 구체적인 사용 사례를 위해 특별히 설계되었기 때문에 많은 효과를 기대하지는 않았습니다.
빙 부조종사가 토한 다소 귀여운 사진
디자인의 주요 목표는 쉽게 직렬화할 수 있는 것이었습니다. 튜링 완전성은 두 가지 작업에만 필요했기 때문에 문제가 되지 않았습니다.
처음에는 Python으로 익명 함수를 작성하는 것부터 시작했습니다. 그러나 작업을 일련의 스레드/프로세스로 확산하려고 시도했을 때 인터프리터는 람다가 직렬화할 수 없다고 불평했습니다. 당시에는 메인 코드 밖에서 살기 위한 로직이 필요해서 결국 그 목적에 맞는 DSL을 만들었습니다.
첫 번째로 떠오른 것은 lisp였습니다. 코드가 배열/목록과 다소 유사한 점이 마음에 들기 때문입니다. 구성을 이미 YAML에 저장했기 때문에 유사성은 좋은 것입니다. 그러므로 논리를 표현하는 새로운 방법을 만들어내는 것에 대해 고민할 필요가 없습니다.
언어를 목록으로 저장하면 또 다른 이점이 있습니다. 처음부터 파서를 만들 필요가 없었습니다. 즉, 어휘 분석(렉서)을 토큰화/수행할 필요가 없었습니다. 즉, 저자는 어휘분석기이다. 구현해야 했던 것은 입력 목록을 가져와서 그것이 프로그램인지(우리는 이를 규칙이라고 함) 컨텍스트에 따라 실행하는 것뿐입니다.
const schema = ["condition.Equal", ["basic.Field", "foo"], ["basic.Field", "bar"]]; // returns a function that checks if context.foo === context.bar const rule = ruler.parse(rule) const context = {foo: "meow", bar: "woof"}; rule(context) // returns false
모든 것이 예상대로 잘 작동했습니다. 그러다가 며칠 전 우연히 Python에서 체계를 구현하는 방법에 대한 기사를 발견했습니다. 아마도 예전에 Clojure를 배우는데 많은 시간을 투자하고 있을 때 이 글을 읽었던 것 같습니다. 하지만 이번에는 처음부터 다시 Python을 사용하여 라이브러리를 다시 구현하기로 결정했습니다.
그래서 이번에는 토큰화하고 어휘 분석기를 직접 수행해야 했습니다. 숫자만 다루면 모든 것이 쉽지만 문자열의 경우에는 상황이 더 복잡해집니다. 나는 또 다른 튜토리얼을 따라가며 make-a-lisp 프로젝트를 재발견했습니다. 결국 포기하고 하이랑에서 제공하는 어휘분석기를 사용했습니다.
렉서는 s-표현식을 취하고 추상 구문 트리와 유사한 구조를 반환합니다. 거기에서 나는 트리를 순회하고 사전을 컨텍스트로 사용하는 클로저로 규칙을 반환하여 파서를 구축합니다.
const schema = ["condition.Equal", ["basic.Field", "foo"], ["basic.Field", "bar"]]; // returns a function that checks if context.foo === context.bar const rule = ruler.parse(rule) const context = {foo: "meow", bar: "woof"}; rule(context) // returns false
제가 직장을 그만둔 지 몇 년이 되었기 때문에 새로운 구현에 실질적인 이점이 없습니다. 내가 남긴 구현은 아마도 현재까지도 잘 작동할 것입니다(수많은 반복을 거친 후에 구현하는 것이 더 좋습니다). 그러나 나는 이 여행을 통해 여전히 한두 가지를 배웁니다. 이 내용이 흥미롭다면 자유롭게 Javascript(배열의 규칙 스키마가 필요한 위치) 또는 새로운 Python 버전(s-expression)을 확인해 보세요.
위 내용은 규칙 엔진 DSL 재작성의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!