В одной статье объясняется, что DSL вам нужно знать о фронтенд-разработке.
В одной статье объясняется, что DSL вам нужно знать о фронтенд-разработке.

Обратите внимание, что эта статья несколько сложна и не подходит для чтения новичкам. Иногда ее читают люди с некоторым опытом программирования. Это полная ерунда, ок, начнем.

Будь то внешние исследования и разработки или внутренние исследования, вы столкнетесь с DSL — это аббревиатура доменно-специфического языка, то есть предметно-ориентированного языка. DSL — это язык, специально разработанный для решения проблем в определенной области. Его синтаксис и семантика специфичны для этой области и не являются универсальными. DSL можно разделить на внутренний DSL и внешний DSL. Внутренний DSL — это DSL, построенный на общем языке программирования, а внешний DSL — это независимый DSL. Эта статья была составлена ​​потому, что на встрече по обмену интерфейсными технологиями, проведенной Tencent в Шэньчжэне, кто-то упомянул DSL, а затем увидел проблемы, которые они решают с помощью DSL, и мне это показалось очень интересным, поэтому я позже изучил DSL.

Что такое ДСЛ?

Итак, что такое DSL? Например, мы знаем, что SQL — это DSL, язык, используемый для работы с базами данных. Его синтаксис и семантика предназначены для операций с базами данных. Другой пример: регулярное выражение также является своего рода DSL. Это язык, используемый для сопоставления строк. Его синтаксис и семантика направлены на сопоставление строк. DSL может помочь нам решить проблемы в некоторых конкретных областях и повысить эффективность нашей разработки. Фактически, HTML, CSS и JavaScript, которые мы используем во внешнем интерфейсе, также можно рассматривать как своего рода DSL. Все они представляют собой языки, используемые для создания веб-приложений. Их синтаксис и семантика специфичны для веб-приложений. не универсальный. Даже популярные интерфейсные фреймворки, такие как React, Vue, Angular и т. д., можно рассматривать как своего рода DSL. Все они представляют собой фреймворки, используемые для создания веб-приложений, и их синтаксис и семантика специфичны для веб-приложений.

Внутренний DSL и внешний DSL

DSL Можно разделить на внутренние помещения. DSL и внешний DSL, внутренний DSL построен на языке программирования общего назначения DSL, а внешний DSL является независимым ДСЛ. внутренний DSL Преимущество состоит в том, что он может использовать преимущества общих языков программирования, таких как проверка типов, завершение кода, отладка и т. д., но его синтаксис и семантика ограничены общими языками программирования. Здесь я приведу вам пример, чтобы вы могли легко его понять. Например, проходим. JavaScript построить ДСЛ, это DSL Может использоваться для описания простого калькулятора, например, это наш DSL:

Язык кода:javascript
копировать
const calculator = {
    add: (a, b) => a + b,
    sub: (a, b) => a - b,
    mul: (a, b) => a * b,
    div: (a, b) => a / b
};

console.log(calculator.add(1, 2)); // 3
console.log(calculator.sub(3, 2)); // 1
console.log(calculator.mul(2, 3)); // 6
console.log(calculator.div(6, 2)); // 3

Этот DSL построен на JavaScript. Его синтаксис и семантика ориентированы на калькуляторы, но на него распространяются ограничения JavaScript. Например, он не может определять новые правила синтаксиса, новые семантические правила и т. д. Внешний DSL не имеет этих ограничений.

Преимущество внешнего DSL заключается в том, что синтаксис и семантика могут быть настроены в соответствии с потребностями предметной области, но затраты на его разработку и обслуживание выше. В реальной разработке мы можем выбрать подходящий DSL в соответствии с нашими потребностями, чтобы повысить эффективность разработки. Недостатком является то, что затраты на разработку и обслуживание DSL высоки и требуют определенного технического уровня. Но не паникуйте, я надеюсь, что эта статья поможет вам лучше понять и применить внешний DSL в вашем проекте.

Сценарии применения внешнего DSL

DSL может применяться во многих областях, таких как файлы конфигурации, механизмы шаблонов, механизмы правил, моделирование предметной области и т. д. В реальной разработке мы можем выбрать подходящий DSL в соответствии с нашими потребностями, чтобы повысить эффективность разработки. Ниже я буду использовать пример, встречающийся в реальных исследованиях и разработках, чтобы проиллюстрировать применение внешнего DSL. Прежде чем продолжить, нам, возможно, потребуется сначала разобраться с одним или двумя инструментами. Один из них — это парсер DSL, написанный на js, который называется Nearley. Другой тоже написан на js и называется jison. Оба инструмента используются для анализа DSL, и вы можете выбрать подходящий инструмент в соответствии с вашими потребностями.

Хорошо, позвольте мне привести вам практический пример.

Реальные случаи

В реальных исследованиях и разработках мы столкнемся с некоторыми конкретными проблемами в конкретных областях. Если мы будем использовать общий язык программирования для решения этих проблем, это может оказаться более громоздким. В настоящее время мы можем использовать DSL для решения этих проблем и повышения эффективности нашей разработки. Ниже я буду использовать практический пример, чтобы проиллюстрировать применение внешнего DSL.

Предположим, мы крупная телекоммуникационная компания, и наши клиенты в основном делятся на две категории: стандартные клиенты (стандарт) и премиальные клиенты (премиум). Компания предлагает два основных продукта: «Товар1» и «Товар2». Всякий раз, когда клиент покупает любой из этих двух продуктов, нам необходимо отправить ему контракт.

Содержание контракта генерируется на основе стандартного шаблона («contract_template»), подписывается автоматически («auto») или вручную («manuel») и использует печать компании («company_seal»). Контракт действителен в течение одного года («один_год»), условия оплаты — помесячно («ежемесячно»).

Однако мы не отправляем контракты всем клиентам. Мы отправим контракт только при выполнении следующих условий, предполагая сценарий:

Клиент является премиальным клиентом; кредитный рейтинг клиента больше или равен 700; клиент приобрел «продукт2». В этом сценарии мы можем использовать приведенный выше DSL для описания этого сценария, например:

Язык кода:javascript
копировать
send_contract {
  recipient: {
    name: "customer.name"
    email: "customer.email"
    id_card: "customer.id_card"
    phone: "customer.phone"
  }
  template: "contract_template"
  signature_method: "auto"
  seal_setting: "company_seal"
  valid_period: "one_year"
  payment_terms: "monthly"
  send if customer.type in ["premium"] and customer.credit_rating >= 700 and customer.product in ["product2"]
}

Хорошо, приведенный выше DSL нельзя запустить напрямую. Для его запуска вам все равно понадобится программа. Мы можем использовать Nearley или Jison для написания правил синтаксиса DSL. Здесь мы возьмем jison в качестве примера для написания грамматических правил DSL.

jison — это генератор синтаксического анализатора JavaScript, который генерирует синтаксический анализатор из описания грамматики в стиле BNF. Вот упрощенное синтаксическое правило jison, основанное на предоставленном вами примере DSL:

Язык кода:javascript
копировать
/* Lexical rules */
%lex
%%
\s+                   /* skip whitespace */
"send_contract"       { return 'SEND_CONTRACT'; }
"\{"                  { return '{'; }
"\}"                  { return '}'; }
"\:"                  { return ':'; }
"\""                  { return '"'; }
[a-zA-Z_][a-zA-Z0-9_]*  { return 'IDENTIFIER'; }
"and"                 { return 'AND'; }
"or"                  { return 'OR'; }
"in"                  { return 'IN'; }
">="                  { return 'GE'; } // Сопоставление здесь является жадным, вы можете обратиться к jison Документация
"<="                  { return 'LE'; }
"<"                   { return 'LT'; }
">"                   { return 'GT'; }
"="                   { return 'EQ'; }
[0-9]+                { return 'NUMBER'; }
.                     { return 'INVALID'; }
/lex

/* Operator precedence */
%start expressions
%left OR
%left AND
%left GE LE GT LT EQ
%left IN

%% /* language grammar */

expressions
    : contract
    ;

contract
    : SEND_CONTRACT '{' contract_body '}' { $$ = { type: 'contract', body: $3 }; }
    ;

contract_body
    : recipient template signature_method seal_setting valid_period payment_terms send_condition
    ;

recipient
    : "recipient" ':' '{' recipient_body '}' { $$ = { type: 'recipient', body: $4 }; }
    ;

recipient_body
    : IDENTIFIER ':' '"' IDENTIFIER '"' { $$ = { [$1]: $4 }; }
    ;

template
    : "template" ':' '"' IDENTIFIER '"' { $$ = { type: 'template', name: $4 }; }
    ;

signature_method
    : "signature_method" ':' '"' IDENTIFIER '"' { $$ = { type: 'signature_method', method: $4 }; }
    ;

seal_setting
    : "seal_setting" ':' '"' IDENTIFIER '"' { $$ = { type: 'seal_setting', setting: $4 }; }
    ;

valid_period
    : "valid_period" ':' '"' IDENTIFIER '"' { $$ = { type: 'valid_period', period: $4 }; }
    ;

payment_terms
    : "payment_terms" ':' '"' IDENTIFIER '"' { $$ = { type: 'payment_terms', terms: $4 }; }
    ;

send_condition
    : "send" "if" condition { $$ = { type: 'send_condition', condition: $3 }; }
    ;

condition
    : IDENTIFIER IN '[' IDENTIFIER_list ']' { $$ = { type: 'in_condition', variable: $1, values: $4 }; }
    | IDENTIFIER GE NUMBER { $$ = { type: 'ge_condition', variable: $1, value: $3 }; }
    | IDENTIFIER AND IDENTIFIER { $$ = { type: 'and_condition', left: $1, right: $3 }; }
    ;

IDENTIFIER_list
    : IDENTIFIER ',' IDENTIFIER_list { $$ = [$1].concat($3); }
    | IDENTIFIER { $$ = [$1]; }
    ;

Если вы хотите узнать больше о грамматических и лексических правилах, рекомендуется прочитать книги о принципах компиляции, которые я не буду здесь вдаваться в подробности.

Этот файл определяет лексические правила (в %lex и /lex между) и грамматические правила (между %% и между концом файла). Лексические правила определяют ваше DSL Различные символы (например, ключевые слова, идентификаторы и операторы) в , а также правила синтаксиса определяют, как эти символы объединяются в допустимые выражения.

Этот файл можно скомпилировать в файл JavaScript с помощью инструмента командной строки jison, который затем можно использовать в своем коде для анализа DSL. И какой результат после анализа? Результатом анализа является абстрактное синтаксическое дерево (AST), которое представляет собой древовидную структуру, используемую для представления синтаксической структуры вашего DSL.

Язык кода:javascript
копировать
{
  type: 'contract',
  body: {
    recipient: {
      name: 'customer.name',
      email: 'customer.email',
      id_card: 'customer.id_card',
      phone: 'customer.phone'
    },
    template: { type: 'template', name: 'contract_template' },
    signature_method: { type: 'signature_method', method: 'auto' },
    seal_setting: { type: 'seal_setting', setting: 'company_seal' },
    valid_period: { type: 'valid_period', period: 'one_year' },
    payment_terms: { type: 'payment_terms', terms: 'monthly' },
    send_condition: {
      type: 'send_condition',
      condition: {
        type: 'and_condition',
        left: {
          type: 'and_condition',
          left: { type: 'in_condition', variable: 'customer.type', values: ['premium'] },
          right: { type: 'ge_condition', variable: 'customer.credit_rating', value: 700 }
        },
        right: { type: 'in_condition', variable: 'customer.product', values: ['product2'] }
      }
    }
  }
}

У нас может быть функция, которая выполняет этот AST, например:

Язык кода:javascript
копировать
class ContractSender {
  constructor() {
    this.recipient = {};
    this.template = '';
    this.signatureMethod = '';
    this.sealSetting = '';
    this.validPeriod = '';
    this.paymentTerms = '';
    this.sendCondition = null;
  }

  setRecipient(info) {
    this.recipient = info;
  }

  setTemplate(template) {
    this.template = template;
  }

  setSignatureMethod(method) {
    this.signatureMethod = method;
  }

  setSealSetting(setting) {
    this.sealSetting = setting;
  }

  setValidPeriod(period) {
    this.validPeriod = period;
  }

  setPaymentTerms(terms) {
    this.paymentTerms = terms;
  }

  setSendCondition(condition) {
    this.sendCondition = condition;
  }

  send() {
    if (this.sendCondition()) {
      console.log(`Sending contract to ${this.recipient.name}...`);
      // Here you would actually send the contract.
    } else {
      console.log(`Not sending contract to ${this.recipient.name}.`);
    }
  }
}

function execute(ast) {
  const sender = new ContractSender();

  sender.setRecipient(ast.body.recipient);
  sender.setTemplate(ast.body.template.name);
  sender.setSignatureMethod(ast.body.signature_method.method);
  sender.setSealSetting(ast.body.seal_setting.setting);
  sender.setValidPeriod(ast.body.valid_period.period);
  sender.setPaymentTerms(ast.body.payment_terms.terms);

  // Here we assume that the send condition is a simple function that returns a boolean.
  // In a real application, you would probably want to convert the send condition from the AST into a function.
  sender.setSendCondition(() => true);

  sender.send();
}

// Parse your DSL into an AST.
const ast = parse(myDSL);

// Execute the AST.
execute(ast);

Таким образом, наш администратор контракта может отправить контракт по DSL. Конечно, вы можете сказать, что этот DSL кажется относительно простым. На самом деле в реальных сценариях он гораздо сложнее, чем этот. Однако, чтобы упростить его и облегчить понимание, я опустил здесь слишком многое. значит надеяться, что вы сможете понять основные принципы DSL. Затем напишите свой собственный DSL, исходя из ваших собственных потребностей.

Это конец?

Нет, когда мы пишем код, если нет раскраски синтаксиса, автодополнения кода и подсказок кода, эффективность нашей разработки будет значительно снижена. Чтобы реализовать раскраску синтаксиса и завершение кода, нам может потребоваться создать собственный язык, чтобы редактор мог нас распознавать. Этот процесс называется языковой поддержкой. Монако Editor и Ace Editor Все они поддерживают поддержку пользовательских языков, и вы можете реализовать поддержку пользовательских языков в соответствии со своими потребностями. Здесь я использую Monaco Editor Возьмите пример, чтобы проиллюстрировать, как реализовать поддержку пользовательского языка.

Monaco Editor — это редактор кода на основе браузера, разработанный Microsoft. Он предоставляет множество мощных функций, включая подсветку синтаксиса, автозаполнение кода, подсказки кода и т. д. Вы можете заставить Monaco Editor поддерживать ваш DSL, определив собственную языковую поддержку.

Вот основные шаги по использованию редактора Monaco для реализации поддержки пользовательских языков:

  1. 1. Определение языковых правил

Во-первых, вам необходимо определить свой DSL грамматические правила. Это можно сделать, создав файл, содержащий tokenizer объект недвижимости для реализации。tokenizer Атрибут — массив, каждый элемент — это массив, содержащий два элемента: регулярное выражение и тип токена. Например:

Язык кода:javascript
копировать
const myDSL = {
  tokenizer: {
    root: [
      [/send_contract/, "keyword"],
      [/"/, "string"],
      [/:/, "delimiter"],
      [/[{}]/, "delimiter.bracket"],
      [/[a-zA-Z_][a-zA-Z0-9_]*/, "identifier"],
      [/[0-9]+/, "number"],
      [/\s+/, "white"]
    ],
  },
};
  1. 1. Зарегистрировать язык и тему

Тогда вам нужно позвонить monaco.languages.register и monaco.languages.setMonarchTokensProvider Приходите и зарегистрируйте свой ДСЛ. Вы также можете позвонить monaco.editor.defineTheme определить свой DSL тема. Например:

Язык кода:javascript
копировать
monaco.languages.register({ id: "ct" });

monaco.languages.setMonarchTokensProvider("ct", myDSL);

monaco.editor.defineTheme("ctTheme", {
  base: "vs",
  inherit: true,
  rules: [
    { token: "keyword", foreground: "880000", fontStyle: "bold" },
    { token: "string", foreground: "008800", fontStyle: "italic" },
    { token: "number", foreground: "000088" },
  ],
});
  1. 1. Создать редактор

Наконец, вы можете позвонить monaco.editor.create чтобы создать экземпляр редактора и установить его язык и тему. Например:

Язык кода:javascript
копировать
const editor = monaco.editor.create(document.getElementById("container"), {
  value: "",
  language: "ct",
  theme: "ctTheme",
});

Вышеупомянутое — это лишь основные шаги по реализации подсветки синтаксиса.,Если вы также хотите реализовать автозаполнение кода и подсказки кода,Возможно, вам придется использовать monaco.languages.registerCompletionItemProvider и monaco.languages.registerHoverProvider ждать API. Конкретная реализация зависит от вашего DSL сложность и ваши конкретные потребности. Я не буду здесь вдаваться в подробности, я отвлекся. Если вам интересно, вы можете это проверить. Monaco Editor Для получения дополнительной информации ознакомьтесь с официальной документацией.

Подвести итог

Рассмотрим процесс решения требований DSL в целом:

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose