src/document.cc

changeset 572
5a08d67ed770
parent 571
ec534922f693
child 575
59c0b57e843b
equal deleted inserted replaced
571:ec534922f693 572:5a08d67ed770
644 return true; 644 return true;
645 } 645 }
646 646
647 // ============================================================================= 647 // =============================================================================
648 // ----------------------------------------------------------------------------- 648 // -----------------------------------------------------------------------------
649 #define CHECK_TOKEN_COUNT(N) \ 649 class LDParseError : public std::exception
650 if (tokens.size() != N) \ 650 { PROPERTY (private, str, Error, STR_OPS, STOCK_WRITE)
651 return new LDError (line, "Bad amount of tokens"); 651 PROPERTY (private, str, Line, STR_OPS, STOCK_WRITE)
652 652
653 #define CHECK_TOKEN_NUMBERS(MIN, MAX) \ 653 public:
654 for (int i = MIN; i <= MAX; ++i) \ 654 LDParseError (str line, str a) : m_Error (a), m_Line (line) {}
655 if (!numeric (tokens[i])) \ 655
656 return new LDError (line, fmt ("Token #%1 was `%2`, expected a number", (i + 1), tokens[i])); 656 const char* what() const throw()
657 { return getError().toLocal8Bit().constData();
658 }
659 };
660
661 // =============================================================================
662 // -----------------------------------------------------------------------------
663 void checkTokenCount (str line, const QStringList& tokens, int num)
664 { if (tokens.size() != num)
665 throw LDParseError (line, fmt ("Bad amount of tokens, expected %1, got %2", num, tokens.size()));
666 }
667
668 // =============================================================================
669 // -----------------------------------------------------------------------------
670 void checkTokenNumbers (str line, const QStringList& tokens, int min, int max)
671 { bool ok;
672
673 // Check scientific notation, e.g. 7.99361e-15
674 QRegExp scient ("\\-?[0-9]+\\.[0-9]+e\\-[0-9]+");
675
676 for (int i = min; i <= max; ++i)
677 { tokens[i].toDouble (&ok);
678
679 if (!ok && !scient.exactMatch (tokens[i]))
680 throw LDParseError (line, fmt ("Token #%1 was `%2`, expected a number (matched length: %3)", (i + 1), tokens[i], scient.matchedLength()));
681 }
682 }
657 683
658 // ============================================================================= 684 // =============================================================================
659 // ----------------------------------------------------------------------------- 685 // -----------------------------------------------------------------------------
660 static vertex parseVertex (QStringList& s, const int n) 686 static vertex parseVertex (QStringList& s, const int n)
661 { vertex v; 687 { vertex v;
670 // This is the LDraw code parser function. It takes in a string containing LDraw 696 // This is the LDraw code parser function. It takes in a string containing LDraw
671 // code and returns the object parsed from it. parseLine never returns null, 697 // code and returns the object parsed from it. parseLine never returns null,
672 // the object will be LDError if it could not be parsed properly. 698 // the object will be LDError if it could not be parsed properly.
673 // ----------------------------------------------------------------------------- 699 // -----------------------------------------------------------------------------
674 LDObject* parseLine (str line) 700 LDObject* parseLine (str line)
675 { QStringList tokens = line.split (" ", str::SkipEmptyParts); 701 { try
676 702 { QStringList tokens = line.split (" ", str::SkipEmptyParts);
677 if (tokens.size() <= 0) 703
678 { // Line was empty, or only consisted of whitespace 704 if (tokens.size() <= 0)
679 return new LDEmpty; 705 { // Line was empty, or only consisted of whitespace
680 } 706 return new LDEmpty;
681 707 }
682 if (tokens[0].length() != 1 || tokens[0][0].isDigit() == false) 708
683 return new LDError (line, "Illogical line code"); 709 if (tokens[0].length() != 1 || tokens[0][0].isDigit() == false)
684 710 throw LDParseError (line, "Illogical line code");
685 int num = tokens[0][0].digitValue(); 711
686 712 int num = tokens[0][0].digitValue();
687 switch (num) 713
688 { case 0: 714 switch (num)
689 { // Comment 715 { case 0:
690 str comm = line.mid (line.indexOf ("0") + 1).simplified(); 716 { // Comment
691 717 str comm = line.mid (line.indexOf ("0") + 1).simplified();
692 // Handle BFC statements 718
693 if (tokens.size() > 2 && tokens[1] == "BFC") 719 // Handle BFC statements
694 { for (int i = 0; i < LDBFC::NumStatements; ++i) 720 if (tokens.size() > 2 && tokens[1] == "BFC")
695 if (comm == fmt ("BFC %1", LDBFC::statements [i])) 721 { for (int i = 0; i < LDBFC::NumStatements; ++i)
696 return new LDBFC ( (LDBFC::Type) i); 722 if (comm == fmt ("BFC %1", LDBFC::statements [i]))
697 723 return new LDBFC ( (LDBFC::Type) i);
698 // MLCAD is notorious for stuffing these statements in parts it 724
699 // creates. The above block only handles valid statements, so we 725 // MLCAD is notorious for stuffing these statements in parts it
700 // need to handle MLCAD-style invertnext, clip and noclip separately. 726 // creates. The above block only handles valid statements, so we
701 struct 727 // need to handle MLCAD-style invertnext, clip and noclip separately.
702 { str a; 728 struct
703 LDBFC::Type b; 729 { str a;
704 } BFCData[] = 730 LDBFC::Type b;
705 { { "INVERTNEXT", LDBFC::InvertNext }, 731 } BFCData[] =
706 { "NOCLIP", LDBFC::NoClip }, 732 { { "INVERTNEXT", LDBFC::InvertNext },
707 { "CLIP", LDBFC::Clip } 733 { "NOCLIP", LDBFC::NoClip },
708 }; 734 { "CLIP", LDBFC::Clip }
709 735 };
710 for (const auto& i : BFCData) 736
711 if (comm == "BFC CERTIFY " + i.a) 737 for (const auto& i : BFCData)
712 return new LDBFC (i.b); 738 if (comm == "BFC CERTIFY " + i.a)
739 return new LDBFC (i.b);
740 }
741
742 if (tokens.size() > 2 && tokens[1] == "!LDFORGE")
743 { // Handle LDForge-specific types, they're embedded into comments too
744 if (tokens[2] == "VERTEX")
745 { // Vertex (0 !LDFORGE VERTEX)
746 checkTokenCount (line, tokens, 7);
747 checkTokenNumbers (line, tokens, 3, 6);
748
749 LDVertex* obj = new LDVertex;
750 obj->setColor (tokens[3].toLong());
751
752 for_axes (ax)
753 obj->pos[ax] = tokens[4 + ax].toDouble(); // 4 - 6
754
755 return obj;
756 } elif (tokens[2] == "OVERLAY")
757 { checkTokenCount (line, tokens, 9);;
758 checkTokenNumbers (line, tokens, 5, 8);
759
760 LDOverlay* obj = new LDOverlay;
761 obj->setFileName (tokens[3]);
762 obj->setCamera (tokens[4].toLong());
763 obj->setX (tokens[5].toLong());
764 obj->setY (tokens[6].toLong());
765 obj->setWidth (tokens[7].toLong());
766 obj->setHeight (tokens[8].toLong());
767 return obj;
768 }
769 }
770
771 // Just a regular comment:
772 LDComment* obj = new LDComment;
773 obj->text = comm;
774 return obj;
713 } 775 }
714 776
715 if (tokens.size() > 2 && tokens[1] == "!LDFORGE") 777 case 1:
716 { // Handle LDForge-specific types, they're embedded into comments too 778 { // Subfile
717 if (tokens[2] == "VERTEX") 779 checkTokenCount (line, tokens, 15);
718 { // Vertex (0 !LDFORGE VERTEX) 780 checkTokenNumbers (line, tokens, 1, 13);
719 CHECK_TOKEN_COUNT (7) 781
720 CHECK_TOKEN_NUMBERS (3, 6) 782 // Try open the file. Disable g_loadingMainFile temporarily since we're
721 783 // not loading the main file now, but the subfile in question.
722 LDVertex* obj = new LDVertex; 784 bool tmp = g_loadingMainFile;
723 obj->setColor (tokens[3].toLong()); 785 g_loadingMainFile = false;
724 786 LDDocument* load = getDocument (tokens[14]);
725 for_axes (ax) 787 g_loadingMainFile = tmp;
726 obj->pos[ax] = tokens[4 + ax].toDouble(); // 4 - 6 788
727 789 // If we cannot open the file, mark it an error. Note we cannot use LDParseError
728 return obj; 790 // here because the error object needs the document reference.
729 } elif (tokens[2] == "OVERLAY") 791 if (!load)
730 { CHECK_TOKEN_COUNT (9); 792 { LDError* obj = new LDError (line, fmt ("Could not open %1", tokens[14]));
731 CHECK_TOKEN_NUMBERS (5, 8) 793 obj->setFileReferenced (tokens[14]);
732
733 LDOverlay* obj = new LDOverlay;
734 obj->setFileName (tokens[3]);
735 obj->setCamera (tokens[4].toLong());
736 obj->setX (tokens[5].toLong());
737 obj->setY (tokens[6].toLong());
738 obj->setWidth (tokens[7].toLong());
739 obj->setHeight (tokens[8].toLong());
740 return obj; 794 return obj;
741 } 795 }
742 } 796
743 797 LDSubfile* obj = new LDSubfile;
744 // Just a regular comment: 798 obj->setColor (tokens[1].toLong());
745 LDComment* obj = new LDComment; 799 obj->setPosition (parseVertex (tokens, 2)); // 2 - 4
746 obj->text = comm; 800
747 return obj; 801 matrix transform;
748 } 802
749 803 for (int i = 0; i < 9; ++i)
750 case 1: 804 transform[i] = tokens[i + 5].toDouble(); // 5 - 13
751 { // Subfile 805
752 CHECK_TOKEN_COUNT (15) 806 obj->setTransform (transform);
753 CHECK_TOKEN_NUMBERS (1, 13) 807 obj->setFileInfo (load);
754
755 // Try open the file. Disable g_loadingMainFile temporarily since we're
756 // not loading the main file now, but the subfile in question.
757 bool tmp = g_loadingMainFile;
758 g_loadingMainFile = false;
759 LDDocument* load = getDocument (tokens[14]);
760 g_loadingMainFile = tmp;
761
762 // If we cannot open the file, mark it an error
763 if (!load)
764 { LDError* obj = new LDError (line, fmt ("Could not open %1", tokens[14]));
765 obj->setFileReferenced (tokens[14]);
766 return obj; 808 return obj;
767 } 809 }
768 810
769 LDSubfile* obj = new LDSubfile; 811 case 2:
770 obj->setColor (tokens[1].toLong()); 812 { checkTokenCount (line, tokens, 8);
771 obj->setPosition (parseVertex (tokens, 2)); // 2 - 4 813 checkTokenNumbers (line, tokens, 1, 7);
772 814
773 matrix transform; 815 // Line
774 816 LDLine* obj = new LDLine;
775 for (int i = 0; i < 9; ++i) 817 obj->setColor (tokens[1].toLong());
776 transform[i] = tokens[i + 5].toDouble(); // 5 - 13 818
777 819 for (int i = 0; i < 2; ++i)
778 obj->setTransform (transform); 820 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 7
779 obj->setFileInfo (load); 821
780 return obj; 822 return obj;
781 } 823 }
782 824
783 case 2: 825 case 3:
784 { CHECK_TOKEN_COUNT (8) 826 { checkTokenCount (line, tokens, 11);
785 CHECK_TOKEN_NUMBERS (1, 7) 827 checkTokenNumbers (line, tokens, 1, 10);
786 828
787 // Line 829 // Triangle
788 LDLine* obj = new LDLine; 830 LDTriangle* obj = new LDTriangle;
789 obj->setColor (tokens[1].toLong()); 831 obj->setColor (tokens[1].toLong());
790 832
791 for (int i = 0; i < 2; ++i) 833 for (int i = 0; i < 3; ++i)
792 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 7 834 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 10
793 835
794 return obj; 836 return obj;
795 } 837 }
796 838
797 case 3: 839 case 4:
798 { CHECK_TOKEN_COUNT (11) 840 case 5:
799 CHECK_TOKEN_NUMBERS (1, 10) 841 { checkTokenCount (line, tokens, 14);
800 842 checkTokenNumbers (line, tokens, 1, 13);
801 // Triangle 843
802 LDTriangle* obj = new LDTriangle; 844 // Quadrilateral / Conditional line
803 obj->setColor (tokens[1].toLong()); 845 LDObject* obj;
804 846
805 for (int i = 0; i < 3; ++i) 847 if (num == 4)
806 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 10 848 obj = new LDQuad;
807 849 else
808 return obj; 850 obj = new LDCondLine;
809 } 851
810 852 obj->setColor (tokens[1].toLong());
811 case 4: 853
812 case 5: 854 for (int i = 0; i < 4; ++i)
813 { CHECK_TOKEN_COUNT (14) 855 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 13
814 CHECK_TOKEN_NUMBERS (1, 13) 856
815 857 return obj;
816 // Quadrilateral / Conditional line 858 }
817 LDObject* obj; 859
818 860 default: // Strange line we couldn't parse
819 if (num == 4) 861 throw LDError (line, "Unknown line code number");
820 obj = new LDQuad; 862 }
821 else 863 }
822 obj = new LDCondLine; 864 catch (LDParseError& e)
823 865 { return new LDError (e.getLine(), e.getError());
824 obj->setColor (tokens[1].toLong());
825
826 for (int i = 0; i < 4; ++i)
827 obj->setVertex (i, parseVertex (tokens, 2 + (i * 3))); // 2 - 13
828
829 return obj;
830 }
831
832 default: // Strange line we couldn't parse
833 return new LDError (line, "Unknown line code number");
834 } 866 }
835 } 867 }
836 868
837 // ============================================================================= 869 // =============================================================================
838 // ----------------------------------------------------------------------------- 870 // -----------------------------------------------------------------------------

mercurial