• S sverchok
  • Информация о проекте
    • Информация о проекте
    • Активность
    • Метки
    • Участники
  • Репозиторий
    • Репозиторий
    • Файлы
    • Коммиты
    • Ветки
    • Теги
    • Участники
    • Диаграмма
    • Сравнение
  • Задачи 148
    • Задачи 148
    • Список
    • Доски
    • Спринты
  • Запросы на слияние 21
    • Запросы на слияние 21
  • CI/CD
    • CI/CD
    • Конвейеры
    • Задания
    • Расписания
  • Развертывания
    • Развертывания
    • Окружения
    • Релизы
  • Пакеты и реестры
    • Пакеты и реестры
    • Реестр пакетов
    • Реестр контейнеров
  • Мониторинг
    • Мониторинг
    • Инциденты
  • Аналитика
    • Аналитика
    • Поток ценности
    • CI/CD
    • Репозиторий
  • Wiki
    • Wiki
  • Сниппеты
    • Сниппеты
  • Активность
  • Диаграмма
  • Создать новую задачу
  • Задания
  • Коммиты
  • Доски с задачами
Свернуть панель
  • nikitronn
  • sverchok
  • Запросы на слияние
  • !2450

Profile node mk3

  • Ревью изменений

  • Скачать
  • Почтовые патчи
  • Простое отличие
Слиты nikitronn запросил слияние profile_mk3 в master Июн 12, 2019
  • Обзор 29
  • Коммиты 41
  • Конвейеры 0
  • Изменения 32

Created by: portnov

Addressed problem description

Current Profile node has the following problems:

  • Very restricted expression syntax - for example, one cannot even use sin or abs
  • Restricted variable names - only one-letter names
  • Comments can start only in the beginning of the line
  • Knots/KnotsNames outputs work only when "from selection" feature is used, nor for any user-provided profile.
  • Old-style "a la Basic interpreter" implementation makes it hard to maintain and add new features.

Solution description

This is in fact a re-implementation of most of the Profile mk2 node:

  • I wrote a very simplistic parsing framework, based on combinatorical parsing. This makes parsing code much simpler to read and maintain. Framework is in sverchok.utils.parsec module. It can be used by other parts of Sverchok if someone wants to implement another kind of DSL for specific node.
  • Split parsing and interpretation to different classes and stages of work.
  • Trailing comments are supported.
  • Input sockets handling is implemented more like "mesh expression" node.
  • Each segment provides data about it's knots, so Knots/KnotNames outputs work for any profile, not only when "from selection" is used.
  • I added some profile examples into profile_examples directory, and an import operator to node's N panel (similar to scripted nodes).
  • As for "from selection" operator... well, for now I can not understand how exactly it works. It is mostly copy-pasted from mk2 implementation.

Parsing framework

This framework implements the approach known as "combinatorial parsing", see https://en.wikipedia.org/wiki/Parser_combinator for starters.

A parser is a function, that takes a string to be parsed, and yields a pair: a parsed object and the rest of the string. It can yield several pairs if the string can be parsed in several ways.

We say that parser succeeds or applies if it yields at least one pair.

We say that parser fails if it does not yield anything.

If a call of parser("var = value") yields, for example, a pair (Variable("var"), "= value"), we say that the parser returned a Variable("var") object; it consumed the "var " string and retained "= value" string to be parsed by subsequential parsers.

The common pattern

    parser = ...
    for value, rest in parser(src):
        if ...
            yield ..., rest

means: apply parser, then analyze returned value, then yield something and the rest of the string.

A parser combinator is a function that takes one or several parsers, and returns another parser. The returned parser is somehow combined from the parsers provided. Parser combinator may, for example, apply several parsers sequentionally, or try one parser and then try another, or something like that.

This module provides minimalistic set of standard parsers and parser combinators.

It still has poor error detection/recovery. Maybe some day...

Profile description DSL

It did not change much since mk2. The most notable changes are:

  • Long variable names are supported
  • Large part of Python's expression syntax is supported, including some restricted subset of standard functions
  • Expressions which are more complex than just variable name or variable name with negation, have to be enclosed in curly brackets, for example {a+1} or {rho * cos(phi)}.
  • New commands supported: H/h, V/v, S/s, Q/q, T/t.
  • C/c, S/s, Q/q, T/t commands support multiple segments, as in SVG.
  • Possibility to set default values for variables ("default" statement) and to introduce temporary variables, expressed via others, for later usage ("let" statement).
  • Statements may optionally be separated with semicolons (;). For some commands (namely: H/h, V/v) trailing semicolon is required!

In general, I had the following ideas in mind while creating new version of this DSL:

  • Compatibility with previous version would be good where possible, but is not exactly necessary, since there will be some migration process anyway.
  • Better compatibility with SVG specification - we are still far from it, but nearer then before.
  • More developer-friendly: long names, more lenient spaces and comments, more functions available in expressions, temporary variables ("let") and default values.

Our DSL has relatively simple BNF:

    <Profile> ::= <Statement> *
    <Statement> ::= <Default> | <Assign>
                    | <MoveTo> | <LineTo> | <CurveTo> | <SmoothLineTo>
                    | <QuadCurveTo> | <SmoothQuadCurveTo>
                    | <ArcTo> | <HorLineTo> | <VertLineTo> | "X"

    <Default> ::= "default" <Variable> "=" <Value>
    <Assign> ::= "let" <Variable> "=" <Value>

    <MoveTo> ::= ("M" | "m") <Value> "," <Value>
    <LineTo> ::= ...
    <CurveTo> ::= ...
    <SmoothCurveTo> ::= ...
    <QuadCurveTo> ::= ...
    <SmoothQuadCurveTo> ::= ...
    <ArcTo> ::= ...
    <HorLineTo> ::= ("H" | "h") <Value> * ";"
    <VertLineTo> ::= ("V" | "v") <Value> * ";"

    <Value> ::= "{" <Expression> "}" | <Variable> | <NegatedVariable> | <Const>
    <Expression> ::= Standard Python expression
    <Variable> ::= Python variable identifier
    <NegatedVariable> ::= "-" <Variable>
    <Const> ::= Python integer or floating-point literal

Profile Mk2 Backwards Compatibility

In some simple cases, profiles made for mk2 will work with mk3 without modifications. However, there are the following incompatibilities:

  • In Mk2, C/c commands had two last additional parameters — "num_verts" and "even_spread". In Mk3, "even_spread" is completely removed, and "num_verts" is now optional, and is specified after "n=", if you need it. The simplest solution: remove last two parameters of each C/c command.
  • In Mk2, A/a commands had last additional argument — "num_segments". In mk3, this parameter is optional and specified after "n=", if you need it. The simplest solution: just remove last parameter of each A/a command.
  • In Mk2, in "c" (relative curveTo) command, handle2 was calculated relative to handle1, and knot2 was calculated relative to handle2. This contradicts SVG specification, so it was changed in Mk3. In Mk3, as per SVG spec, handle1, handle2 and knot2 are all calculated relative to knot1. There is no such simple fix for this: you have to recalculate your parameters of "c" commands. I hope that there were not so much people that used "c" command, as it is not so easy to use (and was even harder in mk2).

Preflight checklist

Put an x letter in each brackets when you're done this item:

  • Code changes complete.
  • Code documentation complete.
  • Documentation for users complete (or not required, if user never sees these changes).
  • Manual testing done.
  • Unit-tests implemented.
  • Ready for merge.
Ответственный
Назначить
Проверяющие
Запросить ревью
Оценка трудозатрат
Исходная ветка: profile_mk3