src/parser.cpp

Sun, 09 Apr 2023 16:30:33 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Sun, 09 Apr 2023 16:30:33 +0300
changeset 367
df61cd3d5ab4
parent 358
ef90ed0a5720
permissions
-rw-r--r--

Also connect up the "Delete" action

/*
 *  LDForge: LDraw parts authoring CAD
 *  Copyright (C) 2013 - 2020 Teemu Piippo
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QRegularExpression>
#include <QIODevice>
#include "src/ldrawalgorithm.h"
#include "src/model.h"
#include "src/parser.h"

#define NUMBER_REGEX R"(([+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))))"
#define SPACE_REGEX R"(\s+)"
#define VEC3_REGEX NUMBER_REGEX SPACE_REGEX NUMBER_REGEX SPACE_REGEX NUMBER_REGEX
#define TWO_VECTORS VEC3_REGEX SPACE_REGEX VEC3_REGEX
#define THREE_VECTORS TWO_VECTORS SPACE_REGEX VEC3_REGEX
#define FOUR_VECTORS THREE_VECTORS SPACE_REGEX VEC3_REGEX

static const auto& exprs()
{
	static const struct
	{
		QRegularExpression subfileRe{QStringLiteral(
			R"(^\s*(1)\s+(\d+)\s+)" FOUR_VECTORS SPACE_REGEX R"(([^ ]+)\s*$)"
		)};
		QRegularExpression edgeRe{QStringLiteral(
			R"(^\s*(2)\s+(\d+)\s+)" TWO_VECTORS R"(\s*$)"
		)};
		QRegularExpression triangleRe{QStringLiteral(
			R"(^\s*(3)\s+(\d+)\s+)" THREE_VECTORS R"(\s*$)"
		)};
		QRegularExpression quadRe{QStringLiteral(
			R"(^\s*(4)\s+(\d+)\s+)" FOUR_VECTORS R"(\s*$)"
		)};
		QRegularExpression cedgeRe{QStringLiteral(
			R"(^\s*(5)\s+(\d+)\s+)" FOUR_VECTORS R"(\s*$)"
		)};
#if 0
		QRegularExpression bfcRe{QStringLiteral(
			R"(^\s*(0) (BFC (?:CERTIFY CCW|CERTIFY CW|NOCERTIFY|INVERTNEXT|CLIP|NOCLIP))\s*$)"
		)};
#endif
		QRegularExpression commentRe{QStringLiteral(
			R"(^\s*(0)\s+(.+)$)"
		)};
	} result;
	return result;
}
template<Attribute Attrib, typename T, Attribute... Attribs>
QString attrib(const LineType<T, Attribs...>& parsed)
{
	const int index = attribIndex<LineType<T, Attribs...>, Attrib>;
	const TextRange& range = parsed.positions[index];
	const QString content = parsed.content;
	return parsed.content.mid(range.start, range.length);
}

template<Attribute X, Attribute Y, Attribute Z, typename T, Attribute... Attribs>
glm::vec3 vectorAttrib(const LineType<T, Attribs...>& parsed)
{
	return glm::vec3{
		attrib<X>(parsed).toFloat(),
		attrib<Y>(parsed).toFloat(),
		attrib<Z>(parsed).toFloat(),
	};
}

template<typename T>
ColorIndex colorAttrib(T* parsed)
{
	return ColorIndex{attrib<Attribute::Color>(*parsed).toInt()};
}

opt<ParsedLine> parse(const QString& line)
{
	const auto tryRe = [&line](const QRegularExpression& re){
		opt<QRegularExpressionMatch> result = re.match(line);
		if (not result->hasMatch()) {
			result.reset();
		}
		return result;
	};
	opt<ParsedLine> result;
	//! \brief Put value into result, add matched ranges to it and return a pointer
	const auto init = [&]<typename T>(T&& value, const QRegularExpressionMatch& match){
		result = value;
		T* parsed = &std::get<T>(*result);
		for (int i = 0; i < countof(T::attributes); ++i) {
			parsed->positions[i] = {
				.start = match.capturedStart(i + 1),
				.length = match.capturedLength(i + 1),
			};
		}
		parsed->content = match.captured(0);
		return parsed;
	};
	if (auto line1Match = tryRe(exprs().commentRe)) {
		LineType0* const parsed = init(LineType0{}, *line1Match);
		parsed->value = Comment{attrib<Attribute::Text>(*parsed)};
	}
	if (auto line1Match = tryRe(exprs().subfileRe)) {
		LineType1* const parsed = init(LineType1{}, *line1Match);
		parsed->value = {
			SubfileReference{
				.name = attrib<Attribute::Name>(*parsed),
				.transformation = glm::mat4{
					glm::vec4{vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed), 0},
					glm::vec4{vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed), 0},
					glm::vec4{vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed), 0},
					glm::vec4{vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed), 1}
				},
			},
			colorAttrib(parsed),
		};
	}
	else if (auto line2Match = tryRe(exprs().edgeRe)) {
		LineType2* const parsed = init(LineType2{}, *line2Match);
		parsed->value = {
			LineSegment{
				.p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed),
				.p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed),
			},
			colorAttrib(parsed),
		};
	}
	else if (auto line3Match = tryRe(exprs().triangleRe)) {
		LineType3* const parsed = init(LineType3{}, *line3Match);
		parsed->value = {
			Triangle{
				.p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed),
				.p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed),
				.p3 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed),
			},
			colorAttrib(parsed),
		};
	}
	else if (auto line4Match = tryRe(exprs().quadRe)) {
		LineType4* const parsed = init(LineType4{}, *line4Match);
		parsed->value = {
			Quadrilateral{
				.p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed),
				.p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed),
				.p3 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed),
				.p4 = vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed),
			},
			colorAttrib(parsed),
		};
	}
	else if (auto line5Match = tryRe(exprs().cedgeRe)) {
		LineType5* const parsed = init(LineType5{}, *line5Match);
		parsed->value = {
			ConditionalEdge{
				.p1 = vectorAttrib<Attribute::X1, Attribute::Y1, Attribute::Z1>(*parsed),
				.p2 = vectorAttrib<Attribute::X2, Attribute::Y2, Attribute::Z2>(*parsed),
				.c1 = vectorAttrib<Attribute::X3, Attribute::Y3, Attribute::Z3>(*parsed),
				.c2 = vectorAttrib<Attribute::X4, Attribute::Y4, Attribute::Z4>(*parsed),
			},
			colorAttrib(parsed),
		};
	}
	return result;
}

#if 0
static QRegExp re{R"((?:(\d+)\\)?(\d+)-(\d)+([a-z]+)\.dat)"};
if (re.exactMatch(name)) {
	const auto p = std::find(std::begin(circularPrimitiveStems), std::end(circularPrimitiveStems), re.cap(4));
	const unsigned int divisions = (re.cap(1).isEmpty()) ? 16 : re.cap(1).toUInt();
	const unsigned int segments = re.cap(2).toUInt() * divisions / re.cap(3).toUInt();
	if (p != std::end(circularPrimitiveStems)) {
		const auto type = static_cast<CircularPrimitive::Type>(p - std::begin(circularPrimitiveStems));
		return Colored<CircularPrimitive>{
			CircularPrimitive{
				.type = type,
				.fraction = {segments, divisions},
				.transformation = transform,
			},
			color,
		};
	}
}
#endif

mercurial