├── images ├── class.png ├── label.png ├── type.png ├── unit.png ├── category.png ├── constant.png ├── folder.png ├── function.png ├── include.png ├── source.png ├── variable.png ├── assembler.png ├── interface.png ├── procedure.png ├── implementation.png └── copyright.txt ├── fonts ├── NotoSans.ttf ├── DejaVuSansMono.ttf ├── Noto.license └── DejaVu.license ├── asm ├── run_coco ├── LisaAsm.keywords ├── LisaAsm.ebnf └── Parser.frame ├── syntax ├── run_coco ├── LisaPascal.keywords ├── Parser.frame └── LisaPascal.ebnf ├── LisaAsm.pro ├── LisaPascal.pro ├── CodeNavigator.qrc ├── AsmSynTree.h ├── AsmSynTree.cpp ├── LisaToken.cpp ├── CodeNavigator.pro ├── Converter.h ├── BUSY ├── LisaRowCol.h ├── LisaToken.h ├── LisaLexer.h ├── LisaHighlighter.h ├── AsmToken.h ├── LisaTokenType.h ├── AsmLexer.h ├── LisaFileSystem.h ├── AsmTokenType.h ├── LisaCodeNavigator.h ├── LisaSynTree.h ├── LisaPpLexer.h ├── AsmPpLexer.h ├── LisaParser.h ├── AsmParser.h ├── LisaAsm.cpp ├── LisaSynTree.cpp ├── Converter.cpp ├── AsmPpLexer.cpp ├── LICENSE.LGPLv3 ├── Readme.md ├── LisaCodeModel.h ├── LisaHighlighter.cpp ├── LisaLexer.cpp └── main.cpp /images/class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/class.png -------------------------------------------------------------------------------- /images/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/label.png -------------------------------------------------------------------------------- /images/type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/type.png -------------------------------------------------------------------------------- /images/unit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/unit.png -------------------------------------------------------------------------------- /fonts/NotoSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/fonts/NotoSans.ttf -------------------------------------------------------------------------------- /images/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/category.png -------------------------------------------------------------------------------- /images/constant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/constant.png -------------------------------------------------------------------------------- /images/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/folder.png -------------------------------------------------------------------------------- /images/function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/function.png -------------------------------------------------------------------------------- /images/include.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/include.png -------------------------------------------------------------------------------- /images/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/source.png -------------------------------------------------------------------------------- /images/variable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/variable.png -------------------------------------------------------------------------------- /images/assembler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/assembler.png -------------------------------------------------------------------------------- /images/interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/interface.png -------------------------------------------------------------------------------- /images/procedure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/procedure.png -------------------------------------------------------------------------------- /fonts/DejaVuSansMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/fonts/DejaVuSansMono.ttf -------------------------------------------------------------------------------- /images/implementation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rochus-keller/LisaPascal/HEAD/images/implementation.png -------------------------------------------------------------------------------- /asm/run_coco: -------------------------------------------------------------------------------- 1 | ../../Coco/Coco ./LisaAsm.atg -o . -namespace Asm 2 | 3 | mv ./Parser.h ../AsmParser.h 4 | mv ./Parser.cpp ../AsmParser.cpp 5 | 6 | mv ./AsmSynTree.h .. 7 | mv ./AsmSynTree.cpp .. 8 | 9 | mv ./AsmTokenType.h .. 10 | mv ./AsmTokenType.cpp .. 11 | 12 | rm ./LisaAsm.atg 13 | -------------------------------------------------------------------------------- /syntax/run_coco: -------------------------------------------------------------------------------- 1 | ../../Coco/Coco ./LisaPascal.atg -trace FP -o . -namespace Lisa > ./coco_out.txt 2 | 3 | mv ./Parser.h ../LisaParser.h 4 | mv ./Parser.cpp ../LisaParser.cpp 5 | 6 | mv ./LisaSynTree.h .. 7 | mv ./LisaSynTree.cpp .. 8 | 9 | mv ./LisaTokenType.h .. 10 | mv ./LisaTokenType.cpp .. 11 | -------------------------------------------------------------------------------- /syntax/LisaPascal.keywords: -------------------------------------------------------------------------------- 1 | and 2 | array 3 | begin 4 | case 5 | const 6 | creation 7 | div 8 | do 9 | downto 10 | else 11 | end 12 | file 13 | for 14 | function 15 | goto 16 | if 17 | implementation 18 | in 19 | interface 20 | intrinsic 21 | label 22 | mod 23 | nil 24 | not 25 | of 26 | or 27 | otherwise 28 | packed 29 | procedure 30 | program 31 | record 32 | repeat 33 | set 34 | string 35 | then 36 | to 37 | type 38 | unit 39 | until 40 | uses 41 | var 42 | while 43 | with 44 | subclass 45 | methods 46 | creation 47 | intrinsic 48 | shared 49 | -------------------------------------------------------------------------------- /LisaAsm.pro: -------------------------------------------------------------------------------- 1 | QT += core 2 | QT -= gui 3 | 4 | TARGET = LisaAsm 5 | CONFIG += console 6 | CONFIG -= app_bundle 7 | 8 | TEMPLATE = app 9 | 10 | INCLUDEPATH += .. 11 | DEFINES += _DEBUG 12 | 13 | SOURCES += \ 14 | LisaAsm.cpp \ 15 | AsmTokenType.cpp \ 16 | AsmLexer.cpp \ 17 | AsmPpLexer.cpp \ 18 | AsmSynTree.cpp \ 19 | AsmParser.cpp \ 20 | LisaFileSystem.cpp \ 21 | LisaLexer.cpp \ 22 | LisaTokenType.cpp \ 23 | LisaToken.cpp 24 | 25 | HEADERS += \ 26 | AsmToken.h \ 27 | AsmTokenType.h \ 28 | AsmLexer.h \ 29 | AsmPpLexer.h \ 30 | AsmSynTree.h \ 31 | AsmParser.h \ 32 | LisaFileSystem.h \ 33 | LisaLexer.h \ 34 | LisaTokenType.h \ 35 | LisaToken.h 36 | 37 | 38 | -------------------------------------------------------------------------------- /LisaPascal.pro: -------------------------------------------------------------------------------- 1 | QT += core 2 | QT -= gui 3 | 4 | TARGET = LisaPascal 5 | CONFIG += console 6 | CONFIG -= app_bundle 7 | 8 | TEMPLATE = app 9 | 10 | INCLUDEPATH += .. 11 | DEFINES += _DEBUG 12 | 13 | SOURCES += main.cpp \ 14 | LisaLexer.cpp \ 15 | LisaParser.cpp \ 16 | LisaSynTree.cpp \ 17 | LisaTokenType.cpp \ 18 | Converter.cpp \ 19 | LisaFileSystem.cpp \ 20 | LisaPpLexer.cpp \ 21 | LisaToken.cpp \ 22 | AsmLexer.cpp \ 23 | AsmTokenType.cpp 24 | 25 | HEADERS += \ 26 | LisaLexer.h \ 27 | LisaParser.h \ 28 | LisaSynTree.h \ 29 | LisaToken.h \ 30 | LisaTokenType.h \ 31 | Converter.h \ 32 | LisaFileSystem.h \ 33 | LisaPpLexer.h \ 34 | AsmLexer.h \ 35 | AsmTokenType.h 36 | -------------------------------------------------------------------------------- /CodeNavigator.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/folder.png 4 | images/unit.png 5 | images/include.png 6 | fonts/DejaVuSansMono.ttf 7 | fonts/NotoSans.ttf 8 | images/constant.png 9 | images/function.png 10 | images/implementation.png 11 | images/interface.png 12 | images/label.png 13 | images/procedure.png 14 | images/type.png 15 | images/variable.png 16 | images/category.png 17 | images/class.png 18 | images/source.png 19 | images/assembler.png 20 | 21 | 22 | -------------------------------------------------------------------------------- /AsmSynTree.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASM_SYNTREE__ 2 | #define __ASM_SYNTREE__ 3 | // This file was automatically generated by EbnfStudio; don't modify it! 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Asm { 10 | 11 | struct SynTree { 12 | enum ParserRule { 13 | R_First = TT_Max + 1, 14 | R_addrop, 15 | R_argument, 16 | R_comment_, 17 | R_directive, 18 | R_expression, 19 | R_factor, 20 | R_filename, 21 | R_line, 22 | R_macrodef, 23 | R_mnemonic, 24 | R_op, 25 | R_program, 26 | R_reg, 27 | R_size, 28 | R_statement, 29 | R_term, 30 | R_Last 31 | }; 32 | SynTree(quint16 r = Tok_Invalid, const Token& = Token() ); 33 | SynTree(const Token& t ):d_tok(t){} 34 | ~SynTree() { foreach(SynTree* n, d_children) delete n; } 35 | 36 | static const char* rToStr( quint16 r ); 37 | 38 | Asm::Token d_tok; 39 | QList d_children; 40 | }; 41 | 42 | } 43 | #endif // __ASM_SYNTREE__ 44 | -------------------------------------------------------------------------------- /AsmSynTree.cpp: -------------------------------------------------------------------------------- 1 | // This file was automatically generated by EbnfStudio; don't modify it! 2 | #include "AsmSynTree.h" 3 | using namespace Asm; 4 | 5 | SynTree::SynTree(quint16 r, const Token& t ):d_tok(r){ 6 | d_tok.d_lineNr = t.d_lineNr; 7 | d_tok.d_colNr = t.d_colNr; 8 | d_tok.d_sourcePath = t.d_sourcePath; 9 | } 10 | 11 | const char* SynTree::rToStr( quint16 r ) { 12 | switch(r) { 13 | case R_addrop: return "addrop"; 14 | case R_argument: return "argument"; 15 | case R_comment_: return "comment"; 16 | case R_directive: return "directive"; 17 | case R_expression: return "expression"; 18 | case R_factor: return "factor"; 19 | case R_filename: return "filename"; 20 | case R_line: return "line"; 21 | case R_macrodef: return "macrodef"; 22 | case R_mnemonic: return "mnemonic"; 23 | case R_op: return "op"; 24 | case R_program: return "program"; 25 | case R_reg: return "reg"; 26 | case R_size: return "size"; 27 | case R_statement: return "statement"; 28 | case R_term: return "term"; 29 | default: if(r 20 | #include 21 | 22 | static QHash d_symbols; 23 | 24 | 25 | const char* Lisa::Token::toId(const QByteArray& ident) 26 | { 27 | if( ident.isEmpty() ) 28 | return ""; 29 | const QByteArray lc = ident.toLower(); 30 | QByteArray& sym = d_symbols[lc]; 31 | if( sym.isEmpty() ) 32 | sym = lc; 33 | return sym.constData(); 34 | } 35 | -------------------------------------------------------------------------------- /CodeNavigator.pro: -------------------------------------------------------------------------------- 1 | QT += core gui widgets 2 | 3 | TARGET = LisaCodeNavigator 4 | TEMPLATE = app 5 | 6 | INCLUDEPATH += .. 7 | 8 | CONFIG(debug, debug|release) { 9 | DEFINES += _DEBUG 10 | } 11 | 12 | !win32{ 13 | QMAKE_CXXFLAGS += -Wno-reorder -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable 14 | } 15 | 16 | HEADERS += \ 17 | LisaLexer.h \ 18 | LisaSynTree.h \ 19 | LisaToken.h \ 20 | LisaTokenType.h \ 21 | LisaHighlighter.h \ 22 | LisaCodeNavigator.h \ 23 | LisaCodeModel.h \ 24 | LisaParser.h \ 25 | LisaRowCol.h \ 26 | LisaFileSystem.h \ 27 | LisaPpLexer.h \ 28 | AsmLexer.h \ 29 | AsmTokenType.h \ 30 | AsmToken.h \ 31 | AsmParser.h \ 32 | AsmPpLexer.h \ 33 | AsmSynTree.h 34 | 35 | SOURCES += \ 36 | LisaLexer.cpp \ 37 | LisaSynTree.cpp \ 38 | LisaTokenType.cpp \ 39 | LisaHighlighter.cpp \ 40 | LisaCodeNavigator.cpp \ 41 | LisaCodeModel.cpp \ 42 | LisaParser.cpp \ 43 | LisaToken.cpp \ 44 | LisaFileSystem.cpp \ 45 | LisaPpLexer.cpp \ 46 | AsmLexer.cpp \ 47 | AsmTokenType.cpp \ 48 | AsmParser.cpp \ 49 | AsmPpLexer.cpp \ 50 | AsmSynTree.cpp 51 | 52 | RESOURCES += \ 53 | CodeNavigator.qrc 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Converter.h: -------------------------------------------------------------------------------- 1 | #ifndef CONVERTER_H 2 | #define CONVERTER_H 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | namespace Lisa 24 | { 25 | class Converter 26 | { 27 | public: 28 | Converter(); 29 | static QStringList collectFiles(const QDir& dir , const QStringList& suffix); 30 | static bool convert( const QDir& fromDir, const QDir& toDir ); 31 | 32 | enum { Unknown, FullUnit, PartialUnit, AnyPascal }; 33 | static int detectPascal( QIODevice* in ); 34 | static bool detectAsm( QIODevice* in ); 35 | static bool detectScript(QIODevice* in ); 36 | private: 37 | 38 | }; 39 | } 40 | 41 | #endif // CONVERTER_H 42 | -------------------------------------------------------------------------------- /BUSY: -------------------------------------------------------------------------------- 1 | # author: Rochus Keller (me@rochus-keller.ch) 2 | # License: GPL 3 | # See https://github.com/rochus-keller/LisaPascal#how-to-build-the-parser-and-code-navigator and 4 | # https://github.com/rochus-keller/BUSY/blob/main/README.md on how to use this file 5 | 6 | if busy_version < "2023-01-15" { 7 | error("this version of BUSY is not compatible with this build") 8 | } 9 | 10 | submod qt = ../LeanQt (HAVE_ITEMVIEWS) 11 | 12 | let run_moc : Moc { 13 | .sources += [ 14 | ./LisaCodeNavigator.h 15 | ./LisaCodeModel.h 16 | ] 17 | } 18 | 19 | let run_rcc : Rcc { 20 | .deps += qt.copy_rcc; 21 | .tool_dir = root_build_dir + relpath(qt); 22 | .sources += ./CodeNavigator.qrc 23 | } 24 | 25 | let exe ! : Executable { 26 | .configs += [ qt.qt_client_config ] 27 | .sources = [ 28 | ./LisaLexer.cpp 29 | ./LisaSynTree.cpp 30 | ./LisaTokenType.cpp 31 | ./LisaHighlighter.cpp 32 | ./LisaCodeNavigator.cpp 33 | ./LisaCodeModel.cpp 34 | ./LisaParser.cpp 35 | ./LisaToken.cpp 36 | ./LisaFileSystem.cpp 37 | ./LisaPpLexer.cpp 38 | ./AsmLexer.cpp 39 | ./AsmTokenType.cpp 40 | ./AsmParser.cpp 41 | ./AsmPpLexer.cpp 42 | ./AsmSynTree.cpp 43 | ] 44 | .include_dirs += [ . .. ] 45 | .deps += [ qt.copy_rcc qt.libqt run_rcc run_moc ] 46 | if target_os == `win32 { 47 | .deps += qt.libqtwinmain 48 | } 49 | .name = "LisaCodeNavigator" 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /LisaRowCol.h: -------------------------------------------------------------------------------- 1 | #ifndef LISAROWCOL 2 | #define LISAROWCOL 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | namespace Lisa 25 | { 26 | struct RowCol 27 | { 28 | enum { ROW_BIT_LEN = 19, COL_BIT_LEN = 32 - ROW_BIT_LEN }; 29 | quint32 d_row : ROW_BIT_LEN; 30 | quint32 d_col : COL_BIT_LEN; 31 | RowCol(int row = 0, int col = 0):d_row(row),d_col(col){} 32 | quint32 packed() const { return ( d_row << COL_BIT_LEN ) | d_col; } 33 | bool operator==(const RowCol& rhs) const { 34 | return d_row == rhs.d_row && d_col == rhs.d_col; 35 | } 36 | bool operator<(const RowCol& rhs) const { 37 | return packed() < rhs.packed(); 38 | } 39 | }; 40 | typedef QPair Range; 41 | typedef QList Ranges; 42 | struct FilePos 43 | { 44 | RowCol d_pos; 45 | QString d_filePath; 46 | FilePos(RowCol pos = RowCol(), const QString& path = QString()):d_pos(pos),d_filePath(path){} 47 | }; 48 | } 49 | 50 | #endif // LISAROWCOL 51 | 52 | -------------------------------------------------------------------------------- /LisaToken.h: -------------------------------------------------------------------------------- 1 | #ifndef LISATOKEN_H 2 | #define LISATOKEN_H 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | 19 | */ 20 | 21 | #include 22 | #include 23 | #include "LisaRowCol.h" 24 | 25 | namespace Lisa 26 | { 27 | struct Token 28 | { 29 | #ifdef _DEBUG 30 | union 31 | { 32 | int d_type; // TokenType 33 | TokenType d_tokenType; 34 | }; 35 | #else 36 | quint8 d_type; // TokenType 37 | #endif 38 | quint8 d_len; 39 | quint32 d_lineNr : RowCol::ROW_BIT_LEN; 40 | quint32 d_colNr : RowCol::COL_BIT_LEN; 41 | QString d_sourcePath; 42 | 43 | QByteArray d_val; 44 | const char* d_id; // lower-case internalized version of d_val 45 | Token(quint16 t = Tok_Invalid, quint32 line = 0, quint16 col = 0, const QByteArray& val = QByteArray()): 46 | d_type(t), d_lineNr(line),d_colNr(col),d_val(val),d_len(0),d_id(0){} 47 | bool isValid() const { return d_type != Tok_Eof && d_type != Tok_Invalid; } 48 | RowCol toLoc() const { return RowCol(d_lineNr,d_colNr); } 49 | 50 | static const char* toId(const QByteArray& ident); 51 | }; 52 | } 53 | 54 | #endif // LISATOKEN_H 55 | -------------------------------------------------------------------------------- /LisaLexer.h: -------------------------------------------------------------------------------- 1 | #ifndef _LISA_LEXER 2 | #define _LISA_LEXER 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace Lisa 25 | { 26 | 27 | class Lexer 28 | { 29 | public: 30 | Lexer(); 31 | ~Lexer(); 32 | 33 | void setStream(QIODevice*, const QString& filePath = QString()); 34 | QIODevice* getDevice() const { return d_in; } 35 | void setIgnoreComments( bool b ) { d_ignoreComments = b; } 36 | void setPackComments( bool b ) { d_packComments = b; } 37 | 38 | Token nextToken(); 39 | Token peekToken(quint8 lookAhead = 1); 40 | QList tokens( const QString& code ); 41 | quint32 getSloc() const { return d_sloc; } 42 | protected: 43 | Token nextTokenImp(); 44 | int skipWhiteSpace(); 45 | void nextLine(); 46 | int lookAhead(int off = 1) const; 47 | Token token(TokenType tt, int len = 1, const QByteArray &val = QByteArray()); 48 | Token ident(); 49 | Token number(); 50 | Token hexnumber(); 51 | Token comment(bool brace = false); 52 | Token string(); 53 | void countLine(); 54 | private: 55 | QIODevice* d_in; 56 | quint32 d_lineNr; 57 | quint16 d_colNr; 58 | QByteArray d_line; 59 | QList d_buffer; 60 | Token d_lastToken; 61 | quint32 d_sloc; // number of lines of code without empty or comment lines 62 | QString d_filePath; 63 | bool d_ignoreComments; // don't deliver comment tokens 64 | bool d_packComments; // Only deliver one Tok_Comment for /**/ instead of Tok_Lcmt and Tok_Rcmt 65 | bool d_lineCounted; 66 | }; 67 | 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /LisaHighlighter.h: -------------------------------------------------------------------------------- 1 | #ifndef LISAHIGHLIGHTER_H 2 | #define LISAHIGHLIGHTER_H 3 | 4 | /* 5 | * Copyright 2023 Rochus Keller 6 | * 7 | * This file is part of the Lisa Pascal Navigator application. 8 | * 9 | * The following is the license that applies to this copy of the 10 | * application. For a license to use the library under conditions 11 | * other than those described here, please email to me@rochus-keller.ch. 12 | * 13 | * GNU General Public License Usage 14 | * This file may be used under the terms of the GNU General Public 15 | * License (GPL) versions 2.0 or 3.0 as published by the Free Software 16 | * Foundation and appearing in the file LICENSE.GPL included in 17 | * the packaging of this file. Please review the following information 18 | * to ensure GNU General Public Licensing requirements will be met: 19 | * http://www.fsf.org/licensing/licenses/info/GPLv2.html and 20 | * http://www.gnu.org/copyleft/gpl.html. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | namespace Lisa 27 | { 28 | class PascalPainter : public QSyntaxHighlighter 29 | { 30 | public: 31 | enum { TokenProp = QTextFormat::UserProperty }; 32 | explicit PascalPainter(QObject *parent = 0); 33 | void addBuiltIn(const QByteArray& bi); 34 | void addKeyword(const QByteArray& kw); 35 | 36 | protected: 37 | QTextCharFormat formatForCategory(int) const; 38 | 39 | // overrides 40 | void highlightBlock(const QString &text); 41 | 42 | private: 43 | enum Category { C_Num, C_Str, C_Kw, C_Type, C_Ident, C_Op, C_Pp, C_Cmt, C_Label, C_Max }; 44 | QTextCharFormat d_format[C_Max]; 45 | QSet d_builtins, d_keywords; 46 | }; 47 | 48 | class AsmPainter : public QSyntaxHighlighter 49 | { 50 | public: 51 | AsmPainter(QObject* parent); 52 | 53 | // overrides 54 | void highlightBlock(const QString &text); 55 | private: 56 | enum Category { C_Num, C_Str, C_Kw, C_Ident, C_Op, C_Pp, C_Cmt, C_Label, C_Max }; 57 | QTextCharFormat d_format[C_Max]; 58 | }; 59 | 60 | class LogPainter : public QSyntaxHighlighter 61 | { 62 | public: 63 | explicit LogPainter(QTextDocument *parent = 0); 64 | protected: 65 | // overrides 66 | void highlightBlock(const QString &text); 67 | }; 68 | } 69 | 70 | #endif // LISAHIGHLIGHTER_H 71 | -------------------------------------------------------------------------------- /AsmToken.h: -------------------------------------------------------------------------------- 1 | #ifndef LISAASMTOKEN 2 | #define LISAASMTOKEN 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | */ 19 | 20 | #include "AsmTokenType.h" 21 | #include "LisaRowCol.h" 22 | 23 | namespace Asm 24 | { 25 | using namespace Lisa; 26 | 27 | struct Token 28 | { 29 | #ifdef _DEBUG 30 | union 31 | { 32 | int d_type; // TokenType 33 | TokenType d_tokenType; 34 | }; 35 | #else 36 | quint8 d_type; // TokenType 37 | #endif 38 | quint32 d_lineNr : RowCol::ROW_BIT_LEN; 39 | quint32 d_colNr : RowCol::COL_BIT_LEN -1; 40 | quint32 d_dotPrefix : 1; 41 | QString d_sourcePath; 42 | 43 | QByteArray d_val; 44 | Token(quint16 t = 0, quint32 line = 0, quint16 col = 0, const QByteArray& val = QByteArray()): 45 | d_type(t), d_lineNr(line),d_colNr(col),d_val(val),d_dotPrefix(0){} 46 | bool isValid() const { return d_type != Tok_Eof && d_type != Tok_Invalid; } 47 | RowCol toLoc() const { return RowCol(d_lineNr,d_colNr); } 48 | static bool isDirective(int t) { 49 | switch( t ) 50 | { 51 | case Tok_PROC: case Tok_FUNC: case Tok_DEF: case Tok_REF: case Tok_SEG: case Tok_ASCII: 52 | case Tok_TITLE: case Tok_END: case Tok_ENDM: case Tok_ELSE: case Tok_ENDC: case Tok_LIST: 53 | case Tok_NOLIST: case Tok_MACROLIST: case Tok_NOMACROLIST: case Tok_PATCHLIST: case Tok_NOPATCHLIST: 54 | case Tok_BYTE: case Tok_WORD: case Tok_ORG: case Tok_RORG: case Tok_IF: case Tok_EQU: 55 | case Tok_INCLUDE: case Tok_MACRO: case Tok_LONG: case Tok_PAGE: 56 | return true; 57 | default: 58 | return false; 59 | } 60 | } 61 | bool isDirective() const { return isDirective(d_type); } 62 | }; 63 | } 64 | 65 | #endif // LISAASMTOKEN 66 | 67 | -------------------------------------------------------------------------------- /LisaTokenType.h: -------------------------------------------------------------------------------- 1 | #ifndef __LISA_TOKENTYPE__ 2 | #define __LISA_TOKENTYPE__ 3 | // This file was automatically generated by EbnfStudio; don't modify it! 4 | 5 | 6 | #include 7 | 8 | #define LISA_CLASCAL 9 | 10 | namespace Lisa { 11 | enum TokenType { 12 | Tok_Invalid = 0, 13 | 14 | TT_Literals, 15 | Tok_Lpar, 16 | Tok_Latt, 17 | Tok_Rpar, 18 | Tok_Star, 19 | Tok_Ratt, 20 | Tok_Plus, 21 | Tok_Comma, 22 | Tok_Minus, 23 | Tok_Dot, 24 | Tok_2Dot, 25 | Tok_Slash, 26 | Tok_Colon, 27 | Tok_ColonEq, 28 | Tok_Semi, 29 | Tok_Lt, 30 | Tok_Leq, 31 | Tok_LtGt, 32 | Tok_Eq, 33 | Tok_Gt, 34 | Tok_Geq, 35 | Tok_At, 36 | Tok_Lbrack, 37 | Tok_Rbrack, 38 | Tok_Hat, 39 | Tok_Lbrace, 40 | Tok_Rbrace, 41 | 42 | TT_Keywords, 43 | Tok_and, 44 | Tok_array, 45 | Tok_begin, 46 | Tok_case, 47 | Tok_const, 48 | Tok_div, 49 | Tok_do, 50 | Tok_downto, 51 | Tok_else, 52 | Tok_end, 53 | Tok_external, 54 | Tok_file, 55 | Tok_for, 56 | Tok_forward, 57 | Tok_function, 58 | Tok_goto, 59 | Tok_if, 60 | Tok_implementation, 61 | Tok_in, 62 | Tok_inline, 63 | Tok_interface, 64 | Tok_intrinsic, 65 | Tok_label, 66 | Tok_methods, 67 | Tok_mod, 68 | Tok_nil, 69 | Tok_not, 70 | Tok_of, 71 | Tok_or, 72 | Tok_otherwise, 73 | Tok_packed, 74 | Tok_procedure, 75 | Tok_program, 76 | Tok_record, 77 | Tok_repeat, 78 | Tok_set, 79 | Tok_shared, 80 | Tok_string, 81 | Tok_subclass, 82 | Tok_then, 83 | Tok_to, 84 | Tok_type, 85 | Tok_unit, 86 | Tok_until, 87 | Tok_uses, 88 | Tok_var, 89 | Tok_while, 90 | Tok_with, 91 | 92 | TT_Specials, 93 | Tok_identifier, 94 | Tok_unsigned_real, 95 | Tok_digit_sequence, 96 | Tok_hex_digit_sequence, 97 | Tok_string_literal, 98 | Tok_Comment, 99 | Tok_Directive, 100 | Tok_Eof, 101 | 102 | TT_MaxToken, 103 | 104 | TT_Max 105 | }; 106 | 107 | const char* tokenTypeString( int ); // Pretty with punctuation chars 108 | const char* tokenTypeName( int ); // Just the names without punctuation chars 109 | bool tokenTypeIsLiteral( int ); 110 | bool tokenTypeIsKeyword( int ); 111 | bool tokenTypeIsSpecial( int ); 112 | TokenType tokenTypeFromString( const QByteArray& str, int* pos = 0 ); 113 | TokenType tokenTypeFromString( const char* str, quint32 len, int* pos = 0 ); 114 | } 115 | #endif // __LISA_TOKENTYPE__ 116 | -------------------------------------------------------------------------------- /AsmLexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LISAASMLEXER_H 2 | #define LISAASMLEXER_H 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | */ 19 | 20 | #include "AsmToken.h" 21 | 22 | #include 23 | #include 24 | 25 | class QIODevice; 26 | 27 | namespace Asm 28 | { 29 | struct Macro 30 | { 31 | QByteArray d_name; 32 | QByteArray d_code; 33 | Macro( const QByteArray& n, const QByteArray& c ):d_name(n),d_code(c){} 34 | Macro(){} 35 | }; 36 | 37 | class Macros : public QHash // name (lower case) -> code 38 | { 39 | public: 40 | 41 | }; 42 | 43 | class Lexer 44 | { 45 | public: 46 | Lexer(); 47 | void setStream(QIODevice*, const QString& filePath = QString()); 48 | QIODevice* getDevice() const { return d_in; } 49 | void setIgnoreComments( bool b ) { d_ignoreComments = b; } 50 | void setPackComments( bool b ) { d_packComments = b; } 51 | void setMacros( Macros* m ) { d_macros = m; } 52 | const QString& getFilePath() const { return d_filePath; } 53 | 54 | Token nextToken(); 55 | Token peekToken(quint8 lookAhead = 1); 56 | QList tokens( const QString& code ); 57 | quint32 getSloc() const { return d_sloc; } 58 | protected: 59 | Token nextTokenImp(); 60 | int skipWhiteSpace(); 61 | void nextLine(); 62 | int lookAhead(int off = 1) const; 63 | Token token(TokenType tt, int len = 1, const QByteArray &val = QByteArray(), bool = false); 64 | Token ident(bool dotPrefix = false); 65 | Token number(); 66 | Token hexnumber(); 67 | Token string1(); 68 | Token string2(); 69 | Token label(); 70 | void countLine(); 71 | Token readMacro(); 72 | bool findMacro(const QByteArray&) const; 73 | private: 74 | QIODevice* d_in; 75 | QString d_filePath; 76 | quint32 d_lineNr; 77 | quint16 d_colNr; 78 | QByteArray d_line; 79 | QList d_buffer; 80 | Token d_lastToken; 81 | quint32 d_sloc; 82 | Macros* d_macros; 83 | bool d_ignoreComments; 84 | bool d_packComments; 85 | bool d_lineCounted; 86 | }; 87 | } 88 | 89 | #endif // LISAASMLEXER_H 90 | -------------------------------------------------------------------------------- /LisaFileSystem.h: -------------------------------------------------------------------------------- 1 | #ifndef FILESYSTEM_H 2 | #define FILESYSTEM_H 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | class QIODevice; 24 | 25 | namespace Lisa 26 | { 27 | class FileSystem : public QObject 28 | { 29 | public: 30 | struct File; 31 | 32 | struct Dir 33 | { 34 | QList d_subdirs; 35 | QList d_files; 36 | QString d_name; 37 | Dir* d_dir; 38 | 39 | void clear(); 40 | void dump(int level = 0) const; 41 | Dir* subdir(const QString& name) const; 42 | const File* file(const QString& name) const; 43 | const File* module(const QByteArray& nameLc) const; 44 | Dir():d_dir(0) {} 45 | ~Dir() { clear(); } 46 | }; 47 | 48 | enum FileType { UnknownFile, PascalProgram, PascalUnit, PascalFragment, AsmUnit, AsmFragment }; 49 | struct File 50 | { 51 | quint8 d_type; 52 | bool d_doublette; 53 | bool d_forceParse; 54 | bool d_parsed; 55 | QString d_realPath; 56 | QString d_name; // fileName 57 | QString d_moduleName; 58 | QByteArray d_moduleLc; // lower-case version 59 | Dir* d_dir; 60 | QString getVirtualPath(bool suffix = true) const; 61 | int level() const; 62 | 63 | File():d_doublette(false),d_type(UnknownFile),d_dir(0),d_forceParse(false),d_parsed(false){} 64 | }; 65 | 66 | explicit FileSystem(QObject *parent = 0); 67 | bool load( const QString& rootDir ); 68 | bool addToRoot( const QStringList& files ); 69 | const QString& getError() const { return d_error; } 70 | const Dir& getRoot() const { return d_root; } 71 | const QString& getRootPath() const { return d_rootDir; } 72 | QList getAllPas() const; 73 | QList getAllAsm() const; 74 | const File* findFile(const QString& realPath) const; 75 | const File* findFile(const Dir* startFrom, const QString& dir, const QString& name) const; 76 | const File* findModule(const Dir* startFrom, const QByteArray& nameLc) const; 77 | 78 | static FileType detectType(QIODevice* in, QByteArray* = 0); 79 | static FileType detectType2(QIODevice* in, QByteArray* = 0); 80 | protected: 81 | bool error( const QString& ); 82 | Dir* getDir( const QString& relPath ); 83 | 84 | private: 85 | QString d_rootDir; 86 | QString d_error; 87 | Dir d_root; 88 | QHash d_fileMap; 89 | QHash d_moduleMap; // module to File* is ambig, but besides "prmgr" (nearly) identical 90 | }; 91 | } 92 | 93 | #endif // FILESYSTEM_H 94 | -------------------------------------------------------------------------------- /AsmTokenType.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASM_TOKENTYPE__ 2 | #define __ASM_TOKENTYPE__ 3 | // This file was automatically generated by EbnfStudio; don't modify it! 4 | 5 | 6 | #include 7 | 8 | #define ASM_ASSUME_UNKNOWN_MACROS 9 | 10 | namespace Asm { 11 | enum TokenType { 12 | Tok_Invalid = 0, 13 | 14 | TT_Literals, 15 | Tok_Hash, 16 | Tok_Amp, 17 | Tok_Lpar, 18 | Tok_Rpar, 19 | Tok_Star, 20 | Tok_Plus, 21 | Tok_Comma, 22 | Tok_Minus, 23 | Tok_Slash, 24 | Tok_Colon, 25 | Tok_Semi, 26 | Tok_Lt, 27 | Tok_LtGt, 28 | Tok_Eq, 29 | Tok_Gt, 30 | Tok_Hat, 31 | Tok_Bar, 32 | Tok_Tilde, 33 | 34 | TT_Keywords, 35 | Tok_ABCD, 36 | Tok_ADD, 37 | Tok_ADDA, 38 | Tok_ADDI, 39 | Tok_ADDQ, 40 | Tok_ADDX, 41 | Tok_ALIGN, 42 | Tok_AND, 43 | Tok_ANDI, 44 | Tok_ASCII, 45 | Tok_ASL, 46 | Tok_ASR, 47 | Tok_BCC, 48 | Tok_BCHG, 49 | Tok_BCLR, 50 | Tok_BCS, 51 | Tok_BEQ, 52 | Tok_BGE, 53 | Tok_BGT, 54 | Tok_BHI, 55 | Tok_BHS, 56 | Tok_BLE, 57 | Tok_BLO, 58 | Tok_BLS, 59 | Tok_BLT, 60 | Tok_BMI, 61 | Tok_BNE, 62 | Tok_BPL, 63 | Tok_BRA, 64 | Tok_BSET, 65 | Tok_BSR, 66 | Tok_BTST, 67 | Tok_BVC, 68 | Tok_BYTE, 69 | Tok_CHK, 70 | Tok_CLR, 71 | Tok_CMP, 72 | Tok_CMPA, 73 | Tok_CMPI, 74 | Tok_CMPM, 75 | Tok_DBEQ, 76 | Tok_DBF, 77 | Tok_DBRA, 78 | Tok_DEF, 79 | Tok_DIVS, 80 | Tok_DIVU, 81 | Tok_ELSE, 82 | Tok_END, 83 | Tok_ENDC, 84 | Tok_ENDM, 85 | Tok_EOR, 86 | Tok_EORI, 87 | Tok_EQU, 88 | Tok_EXG, 89 | Tok_EXT, 90 | Tok_FUNC, 91 | Tok_IF, 92 | Tok_ILLEGAL, 93 | Tok_INCLUDE, 94 | Tok_JMP, 95 | Tok_JSR, 96 | Tok_LEA, 97 | Tok_LINK, 98 | Tok_LIST, 99 | Tok_LONG, 100 | Tok_LSL, 101 | Tok_LSR, 102 | Tok_MACRO, 103 | Tok_MACROLIST, 104 | Tok_MOVE, 105 | Tok_MOVEA, 106 | Tok_MOVEM, 107 | Tok_MOVEP, 108 | Tok_MOVEQ, 109 | Tok_MULS, 110 | Tok_MULU, 111 | Tok_NBCD, 112 | Tok_NEG, 113 | Tok_NEGX, 114 | Tok_NOLIST, 115 | Tok_NOMACROLIST, 116 | Tok_NOP, 117 | Tok_NOPATCHLIST, 118 | Tok_NOT, 119 | Tok_OR, 120 | Tok_ORG, 121 | Tok_ORI, 122 | Tok_PAGE, 123 | Tok_PATCHLIST, 124 | Tok_PEA, 125 | Tok_PROC, 126 | Tok_REF, 127 | Tok_RESET, 128 | Tok_ROL, 129 | Tok_ROR, 130 | Tok_RORG, 131 | Tok_ROXL, 132 | Tok_ROXR, 133 | Tok_RTE, 134 | Tok_RTR, 135 | Tok_RTS, 136 | Tok_SBCD, 137 | Tok_SEG, 138 | Tok_SNE, 139 | Tok_STOP, 140 | Tok_SUB, 141 | Tok_SUBA, 142 | Tok_SUBI, 143 | Tok_SUBQ, 144 | Tok_SUBX, 145 | Tok_SWAP, 146 | Tok_TAS, 147 | Tok_TITLE, 148 | Tok_TRAP, 149 | Tok_TRAPV, 150 | Tok_TST, 151 | Tok_UNLK, 152 | Tok_WORD, 153 | 154 | TT_Specials, 155 | Tok_macrocall, 156 | Tok_number, 157 | Tok_string, 158 | Tok_ident, 159 | Tok_substitute, 160 | Tok_label, 161 | Tok_Comment, 162 | Tok_eol, 163 | Tok_dotW, 164 | Tok_dotL, 165 | Tok_dotB, 166 | Tok_dotS, 167 | Tok_Eof, 168 | 169 | TT_MaxToken, 170 | 171 | TT_Max 172 | }; 173 | 174 | const char* tokenTypeString( int ); // Pretty with punctuation chars 175 | const char* tokenTypeName( int ); // Just the names without punctuation chars 176 | bool tokenTypeIsLiteral( int ); 177 | bool tokenTypeIsKeyword( int ); 178 | bool tokenTypeIsSpecial( int ); 179 | TokenType tokenTypeFromString( const QByteArray& str, int* pos = 0 ); 180 | } 181 | #endif // __ASM_TOKENTYPE__ 182 | -------------------------------------------------------------------------------- /LisaCodeNavigator.h: -------------------------------------------------------------------------------- 1 | #ifndef LISACODENAVIGATOR_H 2 | #define LISACODENAVIGATOR_H 3 | 4 | /* 5 | * Copyright 2023 Rochus Keller 6 | * 7 | * This file is part of the Lisa Pascal Navigator application. 8 | * 9 | * The following is the license that applies to this copy of the 10 | * application. For a license to use the library under conditions 11 | * other than those described here, please email to me@rochus-keller.ch. 12 | * 13 | * GNU General Public License Usage 14 | * This file may be used under the terms of the GNU General Public 15 | * License (GPL) versions 2.0 or 3.0 as published by the Free Software 16 | * Foundation and appearing in the file LICENSE.GPL included in 17 | * the packaging of this file. Please review the following information 18 | * to ensure GNU General Public Licensing requirements will be met: 19 | * http://www.fsf.org/licensing/licenses/info/GPLv2.html and 20 | * http://www.gnu.org/copyleft/gpl.html. 21 | */ 22 | 23 | #include 24 | #include "LisaRowCol.h" 25 | #include "LisaFileSystem.h" 26 | 27 | class QLabel; 28 | class QPlainTextEdit; 29 | class QTreeView; 30 | class QTreeWidget; 31 | class QModelIndex; 32 | 33 | namespace Lisa 34 | { 35 | class CodeModel; 36 | class ModuleDetailMdl; 37 | class Symbol; 38 | class Declaration; 39 | 40 | class CodeNavigator : public QMainWindow 41 | { 42 | Q_OBJECT 43 | public: 44 | explicit CodeNavigator(QWidget *parent = 0); 45 | ~CodeNavigator(); 46 | 47 | void open( const QString& sourceTreePath); 48 | void logMessage(const QString&); 49 | 50 | protected: 51 | struct Place 52 | { 53 | FilePos d_loc; 54 | quint16 d_yoff; 55 | bool operator==( const Place& rhs ) { return d_loc.d_pos.d_row == rhs.d_loc.d_pos.d_row && 56 | d_loc.d_pos.d_col == rhs.d_loc.d_pos.d_col && d_loc.d_filePath == rhs.d_loc.d_filePath; } 57 | Place(const FilePos& loc, quint16 y ):d_loc(loc),d_yoff(y){} 58 | Place():d_yoff(0) {} 59 | }; 60 | 61 | void createModuleList(); 62 | void createDetails(); 63 | void createUsedBy(); 64 | void createLog(); 65 | void pushLocation( const Place& ); 66 | void showViewer( const Place& ); 67 | void fillUsedBy(Symbol* id, Declaration*); 68 | void setPathTitle(const FileSystem::File* f, int row, int col); 69 | void syncModuleList(); 70 | 71 | // overrides 72 | void closeEvent(QCloseEvent* event); 73 | 74 | protected slots: 75 | void onCursorPositionChanged(); 76 | void onModuleDblClick(const QModelIndex&); 77 | void onItemDblClick(const QModelIndex&); 78 | void onUsedByDblClicked(); 79 | void onGoBack(); 80 | void onGoForward(); 81 | void onGotoLine(); 82 | void onFindInFile(); 83 | void onFindAgain(); 84 | void onGotoDefinition(); 85 | void onOpen(); 86 | void onRunReload(); 87 | void onIncreaseSize(); 88 | void onDecreaseSize(); 89 | 90 | private: 91 | class Viewer; 92 | Viewer* d_view; 93 | QLabel* d_pathTitle; 94 | QPlainTextEdit* d_msgLog; 95 | QTreeView* d_modules; 96 | QTreeView* d_module; 97 | QLabel* d_usedByTitle; 98 | QTreeWidget* d_usedBy; 99 | CodeModel* d_mdl; 100 | ModuleDetailMdl* d_mdl2; 101 | QString d_dir; 102 | 103 | QList d_backHisto; // d_backHisto.last() is current place 104 | QList d_forwardHisto; 105 | bool d_pushBackLock; 106 | }; 107 | } 108 | 109 | #endif // LISACODENAVIGATOR_H 110 | -------------------------------------------------------------------------------- /LisaSynTree.h: -------------------------------------------------------------------------------- 1 | #ifndef __LISA_SYNTREE__ 2 | #define __LISA_SYNTREE__ 3 | // This file was automatically generated by EbnfStudio; don't modify it! 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Lisa { 10 | 11 | struct SynTree { 12 | enum ParserRule { 13 | R_First = TT_Max + 1, 14 | R_LisaPascal, 15 | R_actual_parameter, 16 | R_actual_parameter_list, 17 | R_addition_operator, 18 | R_array_type, 19 | R_assigOrCall, 20 | R_assignment_statement_, 21 | R_block, 22 | R_body_, 23 | R_case_label_list, 24 | R_case_limb, 25 | R_case_statement, 26 | R_class_type, 27 | R_comment_, 28 | R_compound_statement, 29 | R_conditional_statement, 30 | R_constant, 31 | R_constant_declaration, 32 | R_constant_declaration_part, 33 | R_constant_identifier_, 34 | R_dereferencer, 35 | R_enumerated_type, 36 | R_expression, 37 | R_expression_list, 38 | R_factor, 39 | R_field_declaration, 40 | R_field_designator, 41 | R_field_identifier, 42 | R_field_list, 43 | R_file_type, 44 | R_final_value, 45 | R_fixed_part, 46 | R_for_statement, 47 | R_formal_parameter_list, 48 | R_formal_parameter_section, 49 | R_function_declaration, 50 | R_function_heading, 51 | R_function_identifier_, 52 | R_goto_statement, 53 | R_identifier_list, 54 | R_identifier_list2, 55 | R_if_statement, 56 | R_implementation_part, 57 | R_index, 58 | R_index_type, 59 | R_initial_value, 60 | R_interface_part, 61 | R_label_, 62 | R_label_declaration_part, 63 | R_member_group, 64 | R_method_block, 65 | R_method_interface, 66 | R_multiplication_operator, 67 | R_non_regular_unit, 68 | R_ordinal_type, 69 | R_otherwise_clause, 70 | R_parameter_declaration, 71 | R_pointer_type, 72 | R_procedure_and_function_declaration_part, 73 | R_procedure_and_function_interface_part, 74 | R_procedure_declaration, 75 | R_procedure_heading, 76 | R_procedure_identifier_, 77 | R_procedure_statement_, 78 | R_program_, 79 | R_program_heading, 80 | R_program_parameters, 81 | R_qualifier, 82 | R_record_type, 83 | R_regular_unit, 84 | R_relational_operator, 85 | R_repeat_statement, 86 | R_repetitive_statement, 87 | R_result_type, 88 | R_set_literal, 89 | R_set_type, 90 | R_sign, 91 | R_simple_expression, 92 | R_simple_statement, 93 | R_simple_type, 94 | R_size_attribute, 95 | R_statement, 96 | R_statement_part, 97 | R_statement_sequence, 98 | R_string_type, 99 | R_structured_statement, 100 | R_structured_type, 101 | R_subrange_type, 102 | R_subroutine_part, 103 | R_tag_field, 104 | R_term, 105 | R_type_, 106 | R_type_declaration, 107 | R_type_declaration_part, 108 | R_type_identifier, 109 | R_unit_heading, 110 | R_unsigned_integer, 111 | R_unsigned_number, 112 | R_uses_clause, 113 | R_variable_declaration, 114 | R_variable_declaration_part, 115 | R_variable_identifier, 116 | R_variable_reference, 117 | R_variant, 118 | R_variant_part, 119 | R_while_statement, 120 | R_with_statement, 121 | R_Last 122 | }; 123 | SynTree(quint16 r = Tok_Invalid, const Token& = Token() ); 124 | SynTree(const Token& t ):d_tok(t){} 125 | ~SynTree() { foreach(SynTree* n, d_children) delete n; } 126 | 127 | static const char* rToStr( quint16 r ); 128 | 129 | Lisa::Token d_tok; 130 | QList d_children; 131 | }; 132 | 133 | } 134 | #endif // __LISA_SYNTREE__ 135 | -------------------------------------------------------------------------------- /LisaPpLexer.h: -------------------------------------------------------------------------------- 1 | #ifndef PPLEXER_H 2 | #define PPLEXER_H 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | */ 19 | 20 | #include "LisaFileSystem.h" 21 | #include "LisaLexer.h" 22 | #include "LisaRowCol.h" 23 | 24 | class QIODevice; 25 | 26 | namespace Lisa 27 | { 28 | class FileSystem; 29 | 30 | class PpLexer 31 | { 32 | public: 33 | enum PpSym { PpNone, PpIncl, PpSetc, PpIfc, PpElsec, PpEndc }; 34 | typedef QHash PpVars; 35 | struct Include 36 | { 37 | const FileSystem::File* d_inc; // the file to include 38 | QString d_sourcePath; // the file where the include lives 39 | RowCol d_loc; // the pos of the include directive in sourcePath 40 | quint16 d_len; // the len of the include directive 41 | }; 42 | 43 | PpLexer(FileSystem*); 44 | ~PpLexer(); 45 | 46 | bool reset(const QString& filePath); 47 | 48 | Token nextToken(); 49 | Token peekToken(quint8 lookAhead = 1); 50 | quint32 getSloc() const { return d_sloc; } 51 | const QList& getIncludes() const { return d_includes; } 52 | const QHash& getMutes() const { return d_mutes; } 53 | protected: 54 | Token nextTokenImp(); 55 | static PpSym checkPp(QByteArray&); 56 | bool handleInclude(const QByteArray& data, const Token& t); 57 | bool handleSetc(const QByteArray& data); 58 | bool handleIfc(const QByteArray& data); 59 | bool handleElsec(); 60 | bool handleEndc(); 61 | bool error( const QString& msg); 62 | 63 | struct ppstatus 64 | { 65 | bool open; // this is the open condition which renders tokens 66 | bool openSeen; // at least one true condition seen 67 | bool elseSeen; // there was already an else part 68 | ppstatus(bool o = true):open(o),openSeen(false),elseSeen(false){} 69 | }; 70 | 71 | ppstatus ppouter() 72 | { 73 | ppstatus res; 74 | if( d_conditionStack.size() >= 2 ) 75 | res = d_conditionStack[d_conditionStack.size()-2]; 76 | return res; 77 | } 78 | ppstatus ppthis() 79 | { 80 | ppstatus res; 81 | if( !d_conditionStack.isEmpty() ) 82 | res = d_conditionStack.back(); 83 | return res; 84 | } 85 | void ppsetthis(bool open, bool thisIsElse = false) 86 | { 87 | if( !d_conditionStack.isEmpty() ) 88 | { 89 | ppstatus& stat = d_conditionStack.back(); 90 | stat.open = open; 91 | if( thisIsElse ) 92 | stat.elseSeen = true; 93 | if( open ) 94 | stat.openSeen = true; 95 | } 96 | } 97 | private: 98 | struct Level 99 | { 100 | Lexer d_lex; 101 | Ranges d_mutes; 102 | }; 103 | 104 | FileSystem* d_fs; 105 | QList d_stack; 106 | QList d_files; 107 | QList d_buffer; 108 | QString d_err; 109 | quint32 d_sloc; // number of lines of code without empty or comment lines 110 | PpVars d_ppVars; 111 | QList d_conditionStack; 112 | QList d_includes; 113 | QHash d_mutes; 114 | RowCol d_startMute; 115 | }; 116 | } 117 | 118 | #endif // PPLEXER_H 119 | -------------------------------------------------------------------------------- /AsmPpLexer.h: -------------------------------------------------------------------------------- 1 | #ifndef ASMPPLEXER_H 2 | #define ASMPPLEXER_H 3 | 4 | /* 5 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 6 | ** 7 | ** This file is part of the LisaPascal project. 8 | ** 9 | ** $QT_BEGIN_LICENSE:LGPL21$ 10 | ** GNU Lesser General Public License Usage 11 | ** This file may be used under the terms of the GNU Lesser 12 | ** General Public License version 2.1 or version 3 as published by the Free 13 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 14 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 15 | ** following information to ensure the GNU Lesser General Public License 16 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 17 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 18 | */ 19 | 20 | #include "LisaFileSystem.h" 21 | #include "AsmLexer.h" 22 | #include "LisaRowCol.h" 23 | 24 | class QIODevice; 25 | 26 | namespace Lisa 27 | { 28 | class FileSystem; 29 | 30 | } 31 | namespace Asm 32 | { 33 | class PpLexer 34 | { 35 | public: 36 | enum PpSym { PpNone, PpIncl, PpSetc, PpIfc, PpElsec, PpEndc }; 37 | typedef QHash PpVars; 38 | struct Include 39 | { 40 | const FileSystem::File* d_inc; // the file to include 41 | QString d_sourcePath; // the file where the include lives 42 | RowCol d_loc; // the pos of the include directive in sourcePath 43 | quint16 d_len; // the len of the include directive 44 | }; 45 | 46 | PpLexer(FileSystem*); 47 | ~PpLexer(); 48 | 49 | bool reset(const QString& filePath); 50 | 51 | Token nextToken(); 52 | Token peekToken(quint8 lookAhead = 1); 53 | quint32 getSloc() const { return d_sloc; } 54 | const QList& getIncludes() const { return d_includes; } 55 | const QHash& getMutes() const { return d_mutes; } 56 | protected: 57 | Token nextTokenImp(); 58 | 59 | bool handleInclude(Token t); 60 | bool handleSetc(const QByteArray& data); 61 | bool handleIfc(const QByteArray& data); 62 | bool handleElsec(); 63 | bool handleEndc(); 64 | bool error( const QString& msg); 65 | 66 | struct ppstatus 67 | { 68 | bool open; // this is the open condition which renders tokens 69 | bool openSeen; // at least one true condition seen 70 | bool elseSeen; // there was already an else part 71 | ppstatus(bool o = true):open(o),openSeen(false),elseSeen(false){} 72 | }; 73 | 74 | ppstatus ppouter() 75 | { 76 | ppstatus res; 77 | if( d_conditionStack.size() >= 2 ) 78 | res = d_conditionStack[d_conditionStack.size()-2]; 79 | return res; 80 | } 81 | ppstatus ppthis() 82 | { 83 | ppstatus res; 84 | if( !d_conditionStack.isEmpty() ) 85 | res = d_conditionStack.back(); 86 | return res; 87 | } 88 | void ppsetthis(bool open, bool thisIsElse = false) 89 | { 90 | if( !d_conditionStack.isEmpty() ) 91 | { 92 | ppstatus& stat = d_conditionStack.back(); 93 | stat.open = open; 94 | if( thisIsElse ) 95 | stat.elseSeen = true; 96 | if( open ) 97 | stat.openSeen = true; 98 | } 99 | } 100 | private: 101 | struct Level 102 | { 103 | Lexer d_lex; 104 | Ranges d_mutes; 105 | }; 106 | 107 | FileSystem* d_fs; 108 | QList d_stack; 109 | QList d_files; 110 | QList d_buffer; 111 | QString d_err; 112 | quint32 d_sloc; // number of lines of code without empty or comment lines 113 | PpVars d_ppVars; 114 | QList d_conditionStack; 115 | QList d_includes; 116 | QHash d_mutes; 117 | RowCol d_startMute; 118 | Macros d_macros; 119 | }; 120 | } 121 | 122 | #endif // ASMPPLEXER_H 123 | -------------------------------------------------------------------------------- /asm/LisaAsm.ebnf: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Rochus Keller 2 | 3 | // This grammar was synthesized from the "Lisa Workshop 3.0 Users Guide" (1984), the 4 | // "MC 68000 Programmer Reference Manual" (1991) and 5 | // "Programming the 68000 - Macintosh Assembly Language" (1986), and then modified 6 | // until all assembler source files of the Lisa Source Code published by the CHM parsed without error 7 | 8 | #define ASSUME_UNKNOWN_MACROS 9 | 10 | program ::= { line } 11 | 12 | line ::= 13 | #ifdef ASSUME_UNKNOWN_MACROS 14 | { term [size] [addrop] [':'] // term instead of ident to disambiguate with original macrocall 15 | // size because ident "E.TOP.S" in libpl-passet 403 16 | | label [':'] 17 | | '#' expression // also this to simulate macrocall 18 | | '-' addrop // dito 19 | } 20 | #else 21 | { ident [':'] | label [':'] } 22 | #endif 23 | [ directive 24 | | statement 25 | | ',' argument { ',' argument } // instead of macrocall 26 | ] eol 27 | 28 | directive ::= ( // the lexer eats the dot prefix; the parser only sees the token 29 | ( PROC | FUNC ) ident [ ',' number ] 30 | | ( DEF | REF ) ident { ',' ident } 31 | | ( SEG | ASCII | TITLE ) ( string | substitute ) 32 | | ( END | ENDM | ELSE | ENDC | LIST | NOLIST | MACROLIST | NOMACROLIST 33 | | PATCHLIST | NOPATCHLIST | PAGE ) 34 | | ( BYTE | WORD | LONG ) expression { ',' expression } 35 | | ( ORG | RORG | IF | EQU | ALIGN ) expression 36 | | INCLUDE filename 37 | | macrodef 38 | ) 39 | 40 | statement ::= 41 | mnemonic [ size ] [ argument { ',' argument } ] 42 | | macrocall 43 | 44 | mnemonic ::= 45 | BEQ | LSR | BNE | LSL | BGE | 'BLE' | 'DBF' | 'BMI' | 'ROR' | 'ASL' | 'ASR' | 'DBRA' 46 | | 'BVC' | 'BPL' | 'BGT' | 'BLS' | 'BHI' | 'BLT' | 'DBEQ' | 'SNE' | 'ROL' | 'ROXR' | 'ROXL' 47 | | BHS | BLO | BCC | BCS | ORI | ANDI | SUBI | ADDI | EORI | CMPI 48 | | BTST | BCHG | BCLR | BSET | MOVEP | MOVEA | MOVE 49 | | NEGX | CLR | NEG | NOT | EXT | NBCD | SWAP | PEA | ILLEGAL | TAS | TST 50 | | TRAP | LINK | UNLK | RESET | NOP | STOP | RTE | RTS | TRAPV | RTR | JSR 51 | | JMP | MOVEM | LEA | CHK | ADDQ | SUBQ | BRA | BSR | MOVEQ | DIVU | DIVS 52 | | SBCD | OR | SUB | SUBX | SUBA | EOR | CMPM | CMP | CMPA | MULU | MULS 53 | | ABCD | EXG | AND | ADD | ADDX | ADDA 54 | 55 | size ::= dotW | dotL | dotB | dotS 56 | 57 | macrodef ::= MACRO ident 58 | 59 | // macrocall- ::= ident [ expression { ',' expression } ] 60 | // creates ambiguity with preceding idents, therefore simulated in line production 61 | macrocall ::= // just an empty symbol 62 | 63 | argument ::= 64 | \LA: 1:'-'&2:'('\ '-' addrop 65 | | expression [size] [ addrop ] 66 | // size because ident "E.TOP.S" in libpl-passet 394 67 | | '#' expression 68 | | addrop 69 | | label 70 | 71 | addrop ::= '(' expression [ size ] [ ',' reg ] ')' [ '+' | size ] 72 | 73 | reg ::= ident [ size ] | substitute 74 | 75 | expression ::= ['-'] term 76 | 77 | term ::= factor { \LL:2\ op factor } 78 | 79 | factor ::= ident | string | number | '<' expression '>' | substitute | '*' 80 | 81 | filename ::= ident { '/' ident } 82 | 83 | op ::= '+' | '-' | '~' | '^' | '*' | '/' | '|' | '&' | '=' | '<>' 84 | // backslash part of syntax but never used and EbnfStudio has issue 85 | 86 | number ::= // digit { digit } | '$' hexdigit { hexdigit } | hexdigit { hexdigit } 'H' | 87 | // octaldigit { octaldigit } 'O' | bindigit { bindigit } 'B' 88 | 89 | string ::= // '"' any printable char '"' | '\'' any printable char '\'' 90 | 91 | ident ::= // alpha { alpha | digit | '%' | '_' | '.' } 92 | 93 | substitute ::= // '%' digit 94 | 95 | label ::= // '@' digit { digit } 96 | 97 | comment- ::= ';' 98 | Comment ::= 99 | 100 | eol ::= // \n 101 | 102 | dotW ::= // '.W' 103 | dotL ::= // '.L' 104 | dotB ::= // '.B' 105 | dotS ::= // '.S' 106 | 107 | 108 | 109 | // Pragmas 110 | %module ::= 'LisaPascal' 111 | %namespace ::= 'Asm' 112 | -------------------------------------------------------------------------------- /fonts/Noto.license: -------------------------------------------------------------------------------- 1 | SIL Open Font License 2 | 3 | Copyright 2012 Google Inc. All Rights Reserved. 4 | 5 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 6 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 7 | 8 | —————————————————————————————- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | —————————————————————————————- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. 14 | 15 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. 16 | 17 | DEFINITIONS 18 | “Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. 19 | 20 | “Reserved Font Name” refers to any names specified as such after the copyright statement(s). 21 | 22 | “Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s). 23 | 24 | “Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. 25 | 26 | “Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. 27 | 28 | PERMISSION & CONDITIONS 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 30 | 31 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 32 | 33 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 34 | 35 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 36 | 37 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 38 | 39 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. 40 | 41 | TERMINATION 42 | This license becomes null and void if any of the above conditions are not met. 43 | 44 | DISCLAIMER 45 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. 46 | -------------------------------------------------------------------------------- /LisaParser.h: -------------------------------------------------------------------------------- 1 | #ifndef __LISA_PARSER__ 2 | #define __LISA_PARSER__ 3 | // This file was automatically generated by EbnfStudio; don't modify it! 4 | 5 | #include 6 | 7 | namespace Lisa { 8 | 9 | class Scanner { 10 | public: 11 | virtual Token next() = 0; 12 | virtual Token peek(int offset) = 0; 13 | }; 14 | 15 | class Parser { 16 | public: 17 | Parser(Scanner* s):scanner(s) {} 18 | void RunParser(); 19 | SynTree root; 20 | struct Error { 21 | QString msg; 22 | int row, col; 23 | QString path; 24 | Error( const QString& m, int r, int c, const QString& p):msg(m),row(r),col(c),path(p){} 25 | }; 26 | QList errors; 27 | protected: 28 | void LisaPascal(SynTree*); 29 | void program_(SynTree*); 30 | void program_heading(SynTree*); 31 | void program_parameters(SynTree*); 32 | void uses_clause(SynTree*); 33 | void identifier_list2(SynTree*); 34 | void regular_unit(SynTree*); 35 | void unit_heading(SynTree*); 36 | void interface_part(SynTree*); 37 | void implementation_part(SynTree*); 38 | void non_regular_unit(SynTree*); 39 | void block(SynTree*); 40 | void label_declaration_part(SynTree*); 41 | void label_(SynTree*); 42 | void constant_declaration_part(SynTree*); 43 | void constant_declaration(SynTree*); 44 | void constant(SynTree*); 45 | void type_declaration_part(SynTree*); 46 | void type_declaration(SynTree*); 47 | void variable_declaration_part(SynTree*); 48 | void variable_declaration(SynTree*); 49 | void procedure_and_function_interface_part(SynTree*); 50 | void procedure_and_function_declaration_part(SynTree*); 51 | void subroutine_part(SynTree*); 52 | void method_block(SynTree*); 53 | void procedure_declaration(SynTree*); 54 | void body_(SynTree*); 55 | void function_declaration(SynTree*); 56 | void statement_part(SynTree*); 57 | void procedure_heading(SynTree*); 58 | void function_heading(SynTree*); 59 | void result_type(SynTree*); 60 | void formal_parameter_list(SynTree*); 61 | void formal_parameter_section(SynTree*); 62 | void parameter_declaration(SynTree*); 63 | void statement_sequence(SynTree*); 64 | void statement(SynTree*); 65 | void simple_statement(SynTree*); 66 | void assigOrCall(SynTree*); 67 | void goto_statement(SynTree*); 68 | void structured_statement(SynTree*); 69 | void compound_statement(SynTree*); 70 | void repetitive_statement(SynTree*); 71 | void while_statement(SynTree*); 72 | void repeat_statement(SynTree*); 73 | void for_statement(SynTree*); 74 | void initial_value(SynTree*); 75 | void final_value(SynTree*); 76 | void conditional_statement(SynTree*); 77 | void if_statement(SynTree*); 78 | void case_statement(SynTree*); 79 | void case_limb(SynTree*); 80 | void case_label_list(SynTree*); 81 | void otherwise_clause(SynTree*); 82 | void with_statement(SynTree*); 83 | void actual_parameter_list(SynTree*); 84 | void actual_parameter(SynTree*); 85 | void expression(SynTree*); 86 | void simple_expression(SynTree*); 87 | void term(SynTree*); 88 | void factor(SynTree*); 89 | void relational_operator(SynTree*); 90 | void addition_operator(SynTree*); 91 | void multiplication_operator(SynTree*); 92 | void variable_reference(SynTree*); 93 | void qualifier(SynTree*); 94 | void index(SynTree*); 95 | void field_designator(SynTree*); 96 | void dereferencer(SynTree*); 97 | void set_literal(SynTree*); 98 | void member_group(SynTree*); 99 | void type_(SynTree*); 100 | void simple_type(SynTree*); 101 | void ordinal_type(SynTree*); 102 | void string_type(SynTree*); 103 | void size_attribute(SynTree*); 104 | void enumerated_type(SynTree*); 105 | void subrange_type(SynTree*); 106 | void structured_type(SynTree*); 107 | void array_type(SynTree*); 108 | void index_type(SynTree*); 109 | void set_type(SynTree*); 110 | void file_type(SynTree*); 111 | void pointer_type(SynTree*); 112 | void class_type(SynTree*); 113 | void method_interface(SynTree*); 114 | void record_type(SynTree*); 115 | void field_list(SynTree*); 116 | void fixed_part(SynTree*); 117 | void field_declaration(SynTree*); 118 | void variant_part(SynTree*); 119 | void tag_field(SynTree*); 120 | void variant(SynTree*); 121 | void field_identifier(SynTree*); 122 | void variable_identifier(SynTree*); 123 | void type_identifier(SynTree*); 124 | void identifier_list(SynTree*); 125 | void expression_list(SynTree*); 126 | void unsigned_integer(SynTree*); 127 | void unsigned_number(SynTree*); 128 | void sign(SynTree*); 129 | protected: 130 | Token cur; 131 | Token la; 132 | Scanner* scanner; 133 | void next(); 134 | Token peek(int off); 135 | void invalid(const char* what); 136 | bool expect(int tt, bool pkw, const char* where); 137 | void addTerminal(SynTree* st); 138 | }; 139 | } 140 | #endif // include 141 | -------------------------------------------------------------------------------- /fonts/DejaVu.license: -------------------------------------------------------------------------------- 1 | DejaVu Fonts License 2 | 3 | Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. 4 | Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) 5 | 6 | Bitstream Vera Fonts Copyright 7 | ——————————————— 8 | 9 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is 10 | a trademark of Bitstream, Inc. 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining a copy 13 | of the fonts accompanying this license (“Fonts”) and associated 14 | documentation files (the “Font Software”), to reproduce and distribute the 15 | Font Software, including without limitation the rights to use, copy, merge, 16 | publish, distribute, and/or sell copies of the Font Software, and to permit 17 | persons to whom the Font Software is furnished to do so, subject to the 18 | following conditions: 19 | 20 | The above copyright and trademark notices and this permission notice shall 21 | be included in all copies of one or more of the Font Software typefaces. 22 | 23 | The Font Software may be modified, altered, or added to, and in particular 24 | the designs of glyphs or characters in the Fonts may be modified and 25 | additional glyphs or characters may be added to the Fonts, only if the fonts 26 | are renamed to names not containing either the words “Bitstream” or the word 27 | “Vera”. 28 | 29 | This License becomes null and void to the extent applicable to Fonts or Font 30 | Software that has been modified and is distributed under the “Bitstream 31 | Vera” names. 32 | 33 | The Font Software may be sold as part of a larger software package but no 34 | copy of one or more of the Font Software typefaces may be sold by itself. 35 | 36 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS 37 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, 38 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, 39 | TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME 40 | FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING 41 | ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, 42 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 43 | THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE 44 | FONT SOFTWARE. 45 | 46 | Except as contained in this notice, the names of Gnome, the Gnome 47 | Foundation, and Bitstream Inc., shall not be used in advertising or 48 | otherwise to promote the sale, use or other dealings in this Font Software 49 | without prior written authorization from the Gnome Foundation or Bitstream 50 | Inc., respectively. For further information, contact: fonts at gnome dot 51 | org. 52 | 53 | Arev Fonts Copyright 54 | ——————————————— 55 | 56 | Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. 57 | 58 | Permission is hereby granted, free of charge, to any person obtaining 59 | a copy of the fonts accompanying this license (“Fonts”) and 60 | associated documentation files (the “Font Software”), to reproduce 61 | and distribute the modifications to the Bitstream Vera Font Software, 62 | including without limitation the rights to use, copy, merge, publish, 63 | distribute, and/or sell copies of the Font Software, and to permit 64 | persons to whom the Font Software is furnished to do so, subject to 65 | the following conditions: 66 | 67 | The above copyright and trademark notices and this permission notice 68 | shall be included in all copies of one or more of the Font Software 69 | typefaces. 70 | 71 | The Font Software may be modified, altered, or added to, and in 72 | particular the designs of glyphs or characters in the Fonts may be 73 | modified and additional glyphs or characters may be added to the 74 | Fonts, only if the fonts are renamed to names not containing either 75 | the words “Tavmjong Bah” or the word “Arev”. 76 | 77 | This License becomes null and void to the extent applicable to Fonts 78 | or Font Software that has been modified and is distributed under the 79 | “Tavmjong Bah Arev” names. 80 | 81 | The Font Software may be sold as part of a larger software package but 82 | no copy of one or more of the Font Software typefaces may be sold by 83 | itself. 84 | 85 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL 89 | TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | 95 | Except as contained in this notice, the name of Tavmjong Bah shall not 96 | be used in advertising or otherwise to promote the sale, use or other 97 | dealings in this Font Software without prior written authorization 98 | from Tavmjong Bah. For further information, contact: tavmjong @ free 99 | . fr. 100 | -------------------------------------------------------------------------------- /AsmParser.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | // This file was automatically generated by Coco/R; don't modify it. 4 | #if !defined(Asm_COCO_PARSER_H__) 5 | #define Asm_COCO_PARSER_H__ 6 | 7 | #include 8 | #include 9 | 10 | 11 | namespace Asm { 12 | 13 | 14 | class PpLexer; 15 | class Parser { 16 | private: 17 | enum { 18 | _EOF=0, 19 | _T_Literals_=1, 20 | _T_Hash=2, 21 | _T_Amp=3, 22 | _T_Lpar=4, 23 | _T_Rpar=5, 24 | _T_Star=6, 25 | _T_Plus=7, 26 | _T_Comma=8, 27 | _T_Minus=9, 28 | _T_Slash=10, 29 | _T_Colon=11, 30 | _T_Semi=12, 31 | _T_Lt=13, 32 | _T_LtGt=14, 33 | _T_Eq=15, 34 | _T_Gt=16, 35 | _T_Hat=17, 36 | _T_Bar=18, 37 | _T_Tilde=19, 38 | _T_Keywords_=20, 39 | _T_ABCD=21, 40 | _T_ADD=22, 41 | _T_ADDA=23, 42 | _T_ADDI=24, 43 | _T_ADDQ=25, 44 | _T_ADDX=26, 45 | _T_ALIGN=27, 46 | _T_AND=28, 47 | _T_ANDI=29, 48 | _T_ASCII=30, 49 | _T_ASL=31, 50 | _T_ASR=32, 51 | _T_BCC=33, 52 | _T_BCHG=34, 53 | _T_BCLR=35, 54 | _T_BCS=36, 55 | _T_BEQ=37, 56 | _T_BGE=38, 57 | _T_BGT=39, 58 | _T_BHI=40, 59 | _T_BHS=41, 60 | _T_BLE=42, 61 | _T_BLO=43, 62 | _T_BLS=44, 63 | _T_BLT=45, 64 | _T_BMI=46, 65 | _T_BNE=47, 66 | _T_BPL=48, 67 | _T_BRA=49, 68 | _T_BSET=50, 69 | _T_BSR=51, 70 | _T_BTST=52, 71 | _T_BVC=53, 72 | _T_BYTE=54, 73 | _T_CHK=55, 74 | _T_CLR=56, 75 | _T_CMP=57, 76 | _T_CMPA=58, 77 | _T_CMPI=59, 78 | _T_CMPM=60, 79 | _T_DBEQ=61, 80 | _T_DBF=62, 81 | _T_DBRA=63, 82 | _T_DEF=64, 83 | _T_DIVS=65, 84 | _T_DIVU=66, 85 | _T_ELSE=67, 86 | _T_END=68, 87 | _T_ENDC=69, 88 | _T_ENDM=70, 89 | _T_EOR=71, 90 | _T_EORI=72, 91 | _T_EQU=73, 92 | _T_EXG=74, 93 | _T_EXT=75, 94 | _T_FUNC=76, 95 | _T_IF=77, 96 | _T_ILLEGAL=78, 97 | _T_INCLUDE=79, 98 | _T_JMP=80, 99 | _T_JSR=81, 100 | _T_LEA=82, 101 | _T_LINK=83, 102 | _T_LIST=84, 103 | _T_LONG=85, 104 | _T_LSL=86, 105 | _T_LSR=87, 106 | _T_MACRO=88, 107 | _T_MACROLIST=89, 108 | _T_MOVE=90, 109 | _T_MOVEA=91, 110 | _T_MOVEM=92, 111 | _T_MOVEP=93, 112 | _T_MOVEQ=94, 113 | _T_MULS=95, 114 | _T_MULU=96, 115 | _T_NBCD=97, 116 | _T_NEG=98, 117 | _T_NEGX=99, 118 | _T_NOLIST=100, 119 | _T_NOMACROLIST=101, 120 | _T_NOP=102, 121 | _T_NOPATCHLIST=103, 122 | _T_NOT=104, 123 | _T_OR=105, 124 | _T_ORG=106, 125 | _T_ORI=107, 126 | _T_PAGE=108, 127 | _T_PATCHLIST=109, 128 | _T_PEA=110, 129 | _T_PROC=111, 130 | _T_REF=112, 131 | _T_RESET=113, 132 | _T_ROL=114, 133 | _T_ROR=115, 134 | _T_RORG=116, 135 | _T_ROXL=117, 136 | _T_ROXR=118, 137 | _T_RTE=119, 138 | _T_RTR=120, 139 | _T_RTS=121, 140 | _T_SBCD=122, 141 | _T_SEG=123, 142 | _T_SNE=124, 143 | _T_STOP=125, 144 | _T_SUB=126, 145 | _T_SUBA=127, 146 | _T_SUBI=128, 147 | _T_SUBQ=129, 148 | _T_SUBX=130, 149 | _T_SWAP=131, 150 | _T_TAS=132, 151 | _T_TITLE=133, 152 | _T_TRAP=134, 153 | _T_TRAPV=135, 154 | _T_TST=136, 155 | _T_UNLK=137, 156 | _T_WORD=138, 157 | _T_Specials_=139, 158 | _T_macrocall=140, 159 | _T_number=141, 160 | _T_string=142, 161 | _T_ident=143, 162 | _T_substitute=144, 163 | _T_label=145, 164 | _T_Comment=146, 165 | _T_eol=147, 166 | _T_dotW=148, 167 | _T_dotL=149, 168 | _T_dotB=150, 169 | _T_dotS=151, 170 | _T_Eof=152, 171 | _T_MaxToken_=153 172 | }; 173 | int maxT; 174 | 175 | int errDist; 176 | int minErrDist; 177 | 178 | void SynErr(int n, const char* ctx = 0); 179 | void Get(); 180 | void Expect(int n, const char* ctx = 0); 181 | bool StartOf(int s); 182 | void ExpectWeak(int n, int follow); 183 | bool WeakSeparator(int n, int syFol, int repFol); 184 | void SynErr(int line, int col, int n, const char* ctx, const QString&, const QString& path ); 185 | 186 | public: 187 | PpLexer *scanner; 188 | struct Error 189 | { 190 | QString msg; 191 | int row, col; 192 | QString path; 193 | }; 194 | QList errors; 195 | 196 | void error(int row, int col, const QString& msg, const QString& path) 197 | { 198 | Error e; 199 | e.row = row; 200 | e.col = col; 201 | e.msg = msg; 202 | e.path = path; 203 | errors.append(e); 204 | } 205 | 206 | Token d_cur; 207 | Token d_next; 208 | QList d_comments; 209 | struct TokDummy 210 | { 211 | int kind; 212 | }; 213 | TokDummy d_dummy; 214 | TokDummy *la; // lookahead token 215 | 216 | int peek( quint8 la = 1 ); 217 | 218 | void RunParser(); 219 | 220 | 221 | Asm::SynTree d_root; 222 | QStack d_stack; 223 | void addTerminal() { 224 | Asm::SynTree* n = new Asm::SynTree( d_cur ); d_stack.top()->d_children.append(n); 225 | } 226 | 227 | 228 | 229 | Parser(PpLexer *scanner); 230 | ~Parser(); 231 | void SemErr(const char* msg); 232 | 233 | void program(); 234 | void line(); 235 | void term(); 236 | void size(); 237 | void addrop(); 238 | void expression(); 239 | void directive(); 240 | void statement(); 241 | void argument(); 242 | void filename(); 243 | void macrodef(); 244 | void mnemonic(); 245 | void reg(); 246 | void factor(); 247 | void op(); 248 | 249 | void Parse(); 250 | 251 | }; // end Parser 252 | 253 | } // namespace 254 | 255 | 256 | #endif 257 | 258 | -------------------------------------------------------------------------------- /LisaAsm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 3 | ** 4 | ** This file is part of the LisaPascal project. 5 | ** 6 | ** $QT_BEGIN_LICENSE:LGPL21$ 7 | ** GNU Lesser General Public License Usage 8 | ** This file may be used under the terms of the GNU Lesser 9 | ** General Public License version 2.1 or version 3 as published by the Free 10 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 11 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 12 | ** following information to ensure the GNU Lesser General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 14 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 15 | */ 16 | 17 | #include "AsmPpLexer.h" 18 | #include "AsmParser.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | static void dump(QTextStream& out, const Asm::SynTree* node, int level) 25 | { 26 | QByteArray str; 27 | if( node->d_tok.d_type == Asm::Tok_Invalid ) 28 | level--; 29 | else if( node->d_tok.d_type < Asm::SynTree::R_First ) 30 | { 31 | if( Asm::tokenTypeIsKeyword( node->d_tok.d_type ) ) 32 | str = Asm::tokenTypeString(node->d_tok.d_type); 33 | else if( node->d_tok.d_type > Asm::TT_Specials ) 34 | str = QByteArray("\"") + node->d_tok.d_val + QByteArray("\""); 35 | else 36 | str = QByteArray("\"") + Asm::tokenTypeString(node->d_tok.d_type) + QByteArray("\""); 37 | 38 | }else 39 | str = Asm::SynTree::rToStr( node->d_tok.d_type ); 40 | if( !str.isEmpty() ) 41 | { 42 | str += QByteArray("\t") + QFileInfo(node->d_tok.d_sourcePath).baseName().toUtf8() + 43 | ":" + QByteArray::number(node->d_tok.d_lineNr) + 44 | ":" + QByteArray::number(node->d_tok.d_colNr); 45 | QByteArray ws; 46 | for( int i = 0; i < level; i++ ) 47 | ws += "| "; 48 | str = ws + str; 49 | out << str.data() << endl; 50 | } 51 | foreach( Asm::SynTree* sub, node->d_children ) 52 | dump( out, sub, level + 1 ); 53 | } 54 | 55 | QStringList collectFiles( const QDir& dir, const QStringList& suffix ) 56 | { 57 | QStringList res; 58 | QStringList files = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name ); 59 | 60 | foreach( const QString& f, files ) 61 | res += collectFiles( QDir( dir.absoluteFilePath(f) ), suffix ); 62 | 63 | files = dir.entryList( suffix, QDir::Files, QDir::Name ); 64 | foreach( const QString& f, files ) 65 | { 66 | res.append(dir.absoluteFilePath(f)); 67 | } 68 | return res; 69 | } 70 | 71 | int main(int argc, char *argv[]) 72 | { 73 | QCoreApplication a(argc, argv); 74 | 75 | if( a.arguments().size() <= 1 ) 76 | return -1; 77 | 78 | #if 0 79 | QStringList files; 80 | 81 | if( QFileInfo(a.arguments()[1]).isDir() ) 82 | files = collectFiles(a.arguments()[1],QStringList() << "*.asm"); 83 | else 84 | files << a.arguments()[1]; 85 | 86 | int ok = 0; 87 | foreach( const QString& file, files ) 88 | { 89 | qDebug() << "***** lexing" << file; 90 | Asm::Lexer lex; 91 | //lex.setIgnoreComments(false); 92 | QFile in(file); 93 | if( !in.open(QIODevice::ReadOnly) ) 94 | return -1; 95 | lex.setStream(&in); 96 | Asm::Token t = lex.nextToken(); 97 | int n = 0, count = 0; 98 | while( t.isValid() ) 99 | { 100 | #if 0 101 | count++; 102 | if( t.isDirective() && t.d_dotPrefix ) 103 | n++; 104 | if( count == 10 && n == 0 ) 105 | qDebug() << "file has no .DIRECTIVE in the first ten tokens" << QFileInfo(file).baseName(); 106 | // there are 30 files to which this applies, e.g. libhw-cursor, machine, mouse, or libfp-elems68k2 107 | #endif 108 | //if( t.d_type == Asm::Tok_5c ) 109 | qDebug() << t.d_lineNr << t.d_colNr << Asm::tokenTypeName(t.d_type) << t.d_val; 110 | t = lex.nextToken(); 111 | } 112 | if( t.d_type == Asm::Tok_Invalid ) 113 | { 114 | qCritical() << t.d_sourcePath << t.d_lineNr << t.d_colNr << t.d_val; 115 | return -1; 116 | } 117 | } 118 | #else 119 | Lisa::FileSystem fs; 120 | fs.load(a.arguments()[1]); 121 | QList files = fs.getAllAsm(); 122 | int ok = 0; 123 | foreach( const Lisa::FileSystem::File* f, files ) 124 | { 125 | Asm::PpLexer lex(&fs); 126 | lex.reset(f->d_realPath); 127 | Asm::Parser p(&lex); 128 | //qDebug() << "**** parsing" << file; 129 | p.RunParser(); 130 | if( !p.errors.isEmpty() ) 131 | { 132 | foreach( const Asm::Parser::Error& e, p.errors ) 133 | qCritical() << f->getVirtualPath() << e.row << e.col << e.msg; 134 | 135 | }else 136 | { 137 | ok++; 138 | //qDebug() << "ok"; 139 | } 140 | #if 0 141 | QFile out(f->d_realPath + ".st"); 142 | out.open(QIODevice::WriteOnly); 143 | QTextStream s(&out); 144 | dump(s,&p.d_root,0); 145 | #endif 146 | } 147 | #endif 148 | qDebug() << "#### finished with" << ok << "files ok of total" << files.size() << "files"; 149 | 150 | return 0; 151 | } 152 | -------------------------------------------------------------------------------- /LisaSynTree.cpp: -------------------------------------------------------------------------------- 1 | // This file was automatically generated by EbnfStudio; don't modify it! 2 | #include "LisaSynTree.h" 3 | using namespace Lisa; 4 | 5 | SynTree::SynTree(quint16 r, const Token& t ):d_tok(r){ 6 | d_tok.d_lineNr = t.d_lineNr; 7 | d_tok.d_colNr = t.d_colNr; 8 | d_tok.d_sourcePath = t.d_sourcePath; 9 | } 10 | 11 | const char* SynTree::rToStr( quint16 r ) { 12 | switch(r) { 13 | case R_LisaPascal: return "LisaPascal"; 14 | case R_actual_parameter: return "actual_parameter"; 15 | case R_actual_parameter_list: return "actual_parameter_list"; 16 | case R_addition_operator: return "addition_operator"; 17 | case R_array_type: return "array_type"; 18 | case R_assigOrCall: return "assigOrCall"; 19 | case R_assignment_statement_: return "assignment_statement"; 20 | case R_block: return "block"; 21 | case R_body_: return "body_"; 22 | case R_case_label_list: return "case_label_list"; 23 | case R_case_limb: return "case_limb"; 24 | case R_case_statement: return "case_statement"; 25 | case R_class_type: return "class_type"; 26 | case R_comment_: return "comment"; 27 | case R_compound_statement: return "compound_statement"; 28 | case R_conditional_statement: return "conditional_statement"; 29 | case R_constant: return "constant"; 30 | case R_constant_declaration: return "constant_declaration"; 31 | case R_constant_declaration_part: return "constant_declaration_part"; 32 | case R_constant_identifier_: return "constant_identifier"; 33 | case R_dereferencer: return "dereferencer"; 34 | case R_enumerated_type: return "enumerated_type"; 35 | case R_expression: return "expression"; 36 | case R_expression_list: return "expression_list"; 37 | case R_factor: return "factor"; 38 | case R_field_declaration: return "field_declaration"; 39 | case R_field_designator: return "field_designator"; 40 | case R_field_identifier: return "field_identifier"; 41 | case R_field_list: return "field_list"; 42 | case R_file_type: return "file_type"; 43 | case R_final_value: return "final_value"; 44 | case R_fixed_part: return "fixed_part"; 45 | case R_for_statement: return "for_statement"; 46 | case R_formal_parameter_list: return "formal_parameter_list"; 47 | case R_formal_parameter_section: return "formal_parameter_section"; 48 | case R_function_declaration: return "function_declaration"; 49 | case R_function_heading: return "function_heading"; 50 | case R_function_identifier_: return "function_identifier"; 51 | case R_goto_statement: return "goto_statement"; 52 | case R_identifier_list: return "identifier_list"; 53 | case R_identifier_list2: return "identifier_list2"; 54 | case R_if_statement: return "if_statement"; 55 | case R_implementation_part: return "implementation_part"; 56 | case R_index: return "index"; 57 | case R_index_type: return "index_type"; 58 | case R_initial_value: return "initial_value"; 59 | case R_interface_part: return "interface_part"; 60 | case R_label_: return "label_"; 61 | case R_label_declaration_part: return "label_declaration_part"; 62 | case R_member_group: return "member_group"; 63 | case R_method_block: return "method_block"; 64 | case R_method_interface: return "method_interface"; 65 | case R_multiplication_operator: return "multiplication_operator"; 66 | case R_non_regular_unit: return "non_regular_unit"; 67 | case R_ordinal_type: return "ordinal_type"; 68 | case R_otherwise_clause: return "otherwise_clause"; 69 | case R_parameter_declaration: return "parameter_declaration"; 70 | case R_pointer_type: return "pointer_type"; 71 | case R_procedure_and_function_declaration_part: return "procedure_and_function_declaration_part"; 72 | case R_procedure_and_function_interface_part: return "procedure_and_function_interface_part"; 73 | case R_procedure_declaration: return "procedure_declaration"; 74 | case R_procedure_heading: return "procedure_heading"; 75 | case R_procedure_identifier_: return "procedure_identifier"; 76 | case R_procedure_statement_: return "procedure_statement"; 77 | case R_program_: return "program_"; 78 | case R_program_heading: return "program_heading"; 79 | case R_program_parameters: return "program_parameters"; 80 | case R_qualifier: return "qualifier"; 81 | case R_record_type: return "record_type"; 82 | case R_regular_unit: return "regular_unit"; 83 | case R_relational_operator: return "relational_operator"; 84 | case R_repeat_statement: return "repeat_statement"; 85 | case R_repetitive_statement: return "repetitive_statement"; 86 | case R_result_type: return "result_type"; 87 | case R_set_literal: return "set_literal"; 88 | case R_set_type: return "set_type"; 89 | case R_sign: return "sign"; 90 | case R_simple_expression: return "simple_expression"; 91 | case R_simple_statement: return "simple_statement"; 92 | case R_simple_type: return "simple_type"; 93 | case R_size_attribute: return "size_attribute"; 94 | case R_statement: return "statement"; 95 | case R_statement_part: return "statement_part"; 96 | case R_statement_sequence: return "statement_sequence"; 97 | case R_string_type: return "string_type"; 98 | case R_structured_statement: return "structured_statement"; 99 | case R_structured_type: return "structured_type"; 100 | case R_subrange_type: return "subrange_type"; 101 | case R_subroutine_part: return "subroutine_part"; 102 | case R_tag_field: return "tag_field"; 103 | case R_term: return "term"; 104 | case R_type_: return "type_"; 105 | case R_type_declaration: return "type_declaration"; 106 | case R_type_declaration_part: return "type_declaration_part"; 107 | case R_type_identifier: return "type_identifier"; 108 | case R_unit_heading: return "unit_heading"; 109 | case R_unsigned_integer: return "unsigned_integer"; 110 | case R_unsigned_number: return "unsigned_number"; 111 | case R_uses_clause: return "uses_clause"; 112 | case R_variable_declaration: return "variable_declaration"; 113 | case R_variable_declaration_part: return "variable_declaration_part"; 114 | case R_variable_identifier: return "variable_identifier"; 115 | case R_variable_reference: return "variable_reference"; 116 | case R_variant: return "variant"; 117 | case R_variant_part: return "variant_part"; 118 | case R_while_statement: return "while_statement"; 119 | case R_with_statement: return "with_statement"; 120 | default: if(r 20 | using namespace Lisa; 21 | 22 | // stats: ("F", 57)("OBJ", 8)("TEXT", 109)("text", 9)("txt", 1092) 23 | // *.F -> font 24 | // *.OBJ -> object files; source available unless TK-ALERT, TK-NullChange, TK-WorkDir, libtk-passwd and IconEdit 25 | // *.TEXT, *.text -> looks like different kinds of sources covered in binary format 26 | // *.txt -> Lisa Pascal (614/1092) or assembler source files 27 | 28 | // NOTES: 29 | // both (*$ and {$ are used for directives 30 | // there are no $DECL in files categorized as .inc; 31 | // but there are a lot of $IFC in .inc files (even $I in two cases); 32 | // we can assume that the decls of the including file also apply to included files 33 | // each pascal file has the suffix "text.unix.txt" with no exception 34 | 35 | QStringList Converter::collectFiles( const QDir& dir, const QStringList& suffix ) 36 | { 37 | QStringList res; 38 | QStringList files = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name ); 39 | 40 | foreach( const QString& f, files ) 41 | res += collectFiles( QDir( dir.absoluteFilePath(f) ), suffix ); 42 | 43 | files = dir.entryList( suffix, QDir::Files, QDir::Name ); 44 | foreach( const QString& f, files ) 45 | { 46 | res.append(dir.absoluteFilePath(f)); 47 | } 48 | return res; 49 | } 50 | 51 | int Converter::detectPascal( QIODevice* in ) 52 | { 53 | Q_ASSERT(in->reset()); 54 | Lexer lex; 55 | lex.setStream(in); 56 | lex.setIgnoreComments(false); 57 | Token t = lex.nextToken(); 58 | int res = Unknown; 59 | while( t.isValid() ) 60 | { 61 | switch(t.d_type) 62 | { 63 | case Tok_Comment: 64 | if( t.d_val.startsWith("(*") || t.d_val.startsWith("{$") ) 65 | res = AnyPascal; 66 | break; 67 | case Tok_program: 68 | case Tok_unit: 69 | return FullUnit; 70 | case Tok_function: 71 | case Tok_procedure: 72 | case Tok_const: 73 | #ifdef LISA_CLASCAL 74 | case Tok_methods: 75 | #endif 76 | // exceptions: aplw-compflags 77 | return PartialUnit; 78 | default: 79 | return res; 80 | } 81 | 82 | t = lex.nextToken(); 83 | } 84 | return res; 85 | } 86 | 87 | bool Converter::detectAsm( QIODevice* in ) 88 | { 89 | Q_ASSERT(in->reset()); 90 | int lastSemi = -1; 91 | int count = 0; 92 | bool semiStretch = false; 93 | int mnemonic = 0; 94 | for( int i = 0; i < 50; i++ ) 95 | { 96 | const QByteArray line = in->readLine().trimmed().toUpper(); 97 | if( line.startsWith(';') ) 98 | { 99 | if( lastSemi == -1 || lastSemi == (i - 1) ) 100 | { 101 | count++; 102 | lastSemi = i; 103 | if( count >= 15 ) 104 | semiStretch = true; 105 | }else 106 | { 107 | count = 0; 108 | lastSemi = -1; 109 | } 110 | }else 111 | { 112 | count = 0; 113 | lastSemi = -1; 114 | if( line.startsWith(".SEG") || line.startsWith(".PROC") || line.startsWith(".FUNC") 115 | || line.startsWith(".REF") || line.startsWith(".SEG") 116 | || line.startsWith(".WORD") || line.startsWith(".BYTE") 117 | || line.startsWith("BGE.S") || line.startsWith("BNE.S") 118 | || line.startsWith("MOVE.W") || line.startsWith("TST.W") 119 | || line.startsWith("MOVE.L") 120 | || line.contains(".EQU ") 121 | ) 122 | mnemonic++; 123 | } 124 | } 125 | return semiStretch || mnemonic; 126 | } 127 | 128 | bool Converter::detectScript(QIODevice* in ) 129 | { 130 | Q_ASSERT(in->reset()); 131 | const QByteArray line = in->readLine().trimmed().toUpper(); 132 | const bool found = line.startsWith("$EXEC") || line.startsWith("EXEC("); 133 | return found; 134 | } 135 | 136 | // assembler files have asm, 68k in the filename 137 | // exceptions: apll-qicode apll-stgcomp apll-xxfer aplt-convert aplw-fastgen aplw-sp-util appw-xfer 138 | // libhw-cursor libhw-drivers libhw-hwiequ libhw-hwintl libhw-keybd libhw-legends libhw-machine libhw-mouse 139 | // libhw-sprkeybd libhw-timers libam-alertprocs libdb-qsort libfp-bindec1/2/3 140 | // actually all assembler files start with a few lines where each starts with a ';' 141 | // command files start with $EXEC, there are many of these 142 | // menu files start with 1 143 | // clascal files: libtk-uabc4, libtk-uabc3, and seven more in lisa_toolkit/tk3/4/5 144 | 145 | bool Converter::convert(const QDir& fromDir, const QDir& toDir) 146 | { 147 | const int off = fromDir.path().size(); 148 | const QStringList files = collectFiles(fromDir, QStringList() << "*.txt"); 149 | foreach( const QString& f, files ) 150 | { 151 | QString newFilePath = toDir.absoluteFilePath(f.mid(off+1).toLower()); 152 | if( newFilePath.endsWith("text.unix.txt") ) 153 | newFilePath.chop(13); 154 | else if(newFilePath.endsWith("unix.txt") ) 155 | newFilePath.chop(8); 156 | else if(newFilePath.endsWith("txt") ) 157 | newFilePath.chop(3); 158 | const QString name = QFileInfo(f).baseName().toLower(); 159 | const QString newDirPath = QFileInfo(f).dir().path().mid(off+1).toLower(); 160 | if( !newDirPath.isEmpty() ) 161 | toDir.mkpath( newDirPath ); 162 | QFile in(f); 163 | in.open(QIODevice::ReadOnly); 164 | 165 | const int kind = detectPascal(&in); 166 | if( kind == FullUnit ) 167 | newFilePath += "pas"; 168 | else if( kind == PartialUnit || kind == AnyPascal ) 169 | { 170 | newFilePath += "inc"; 171 | //qDebug() << QFileInfo(newFilePath).baseName(); 172 | }else if( detectScript(&in) ) 173 | newFilePath += "sh"; 174 | else if( detectAsm(&in) || name.contains("asm") || name.contains("68k") ) 175 | newFilePath += "asm"; 176 | else 177 | newFilePath += "txt"; 178 | 179 | Q_ASSERT(in.reset()); 180 | QFile out(newFilePath); 181 | if( !out.open(QIODevice::WriteOnly) ) 182 | { 183 | qCritical() << "### cannot open for writing:" << newFilePath; 184 | continue; 185 | } 186 | QByteArray text = in.readAll(); 187 | if( text.endsWith(0xff) ) 188 | text.chop(1); 189 | if( text.size() != out.write(text) ) 190 | { 191 | qCritical() << "### could not write everything to:" << newFilePath; 192 | continue; 193 | } 194 | } 195 | return true; 196 | } 197 | -------------------------------------------------------------------------------- /AsmPpLexer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 3 | ** 4 | ** This file is part of the LisaPascal project. 5 | ** 6 | ** $QT_BEGIN_LICENSE:LGPL21$ 7 | ** GNU Lesser General Public License Usage 8 | ** This file may be used under the terms of the GNU Lesser 9 | ** General Public License version 2.1 or version 3 as published by the Free 10 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 11 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 12 | ** following information to ensure the GNU Lesser General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 14 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 15 | */ 16 | 17 | #include "AsmPpLexer.h" 18 | #include 19 | #include 20 | #include 21 | using namespace Asm; 22 | using namespace Lisa; 23 | 24 | PpLexer::PpLexer(FileSystem* fs):d_fs(fs),d_sloc(0) 25 | { 26 | Q_ASSERT(fs); 27 | } 28 | 29 | PpLexer::~PpLexer() 30 | { 31 | for( int i = 0; i < d_files.size(); i++ ) 32 | delete d_files[i]; 33 | } 34 | 35 | bool PpLexer::reset(const QString& filePath) 36 | { 37 | d_stack.clear(); 38 | d_buffer.clear(); 39 | for( int i = 0; i < d_files.size(); i++ ) 40 | delete d_files[i]; 41 | d_files.clear(); 42 | d_sloc = 0; 43 | d_includes.clear(); 44 | 45 | const FileSystem::File* f = d_fs->findFile(filePath); 46 | if( f == 0 ) 47 | return false; 48 | d_stack.push_back(Level()); 49 | QFile* file = new QFile(filePath); 50 | if( !file->open(QIODevice::ReadOnly) ) 51 | { 52 | delete file; 53 | d_stack.pop_back(); 54 | return false; 55 | } 56 | d_stack.back().d_lex.setStream(file,filePath); 57 | d_stack.back().d_lex.setMacros(&d_macros); 58 | return true; 59 | } 60 | 61 | Token PpLexer::nextToken() 62 | { 63 | Token t; 64 | if( !d_buffer.isEmpty() ) 65 | { 66 | t = d_buffer.first(); 67 | d_buffer.pop_front(); 68 | }else 69 | t = nextTokenImp(); 70 | Q_ASSERT( t.d_type != Tok_Comment ); 71 | return t; 72 | } 73 | 74 | Token PpLexer::peekToken(quint8 lookAhead) 75 | { 76 | Q_ASSERT( lookAhead > 0 ); 77 | while( d_buffer.size() < lookAhead ) 78 | { 79 | Token t = nextTokenImp(); 80 | Q_ASSERT( t.d_type != Tok_Comment ); 81 | d_buffer.push_back( t ); 82 | } 83 | return d_buffer[ lookAhead - 1 ]; 84 | } 85 | 86 | Token PpLexer::nextTokenImp() 87 | { 88 | if( d_stack.isEmpty() ) 89 | return Token(Tok_Eof); 90 | Token t = d_stack.back().d_lex.nextToken(); 91 | while( t.d_type == Tok_Comment || t.d_type == Tok_Eof ) 92 | { 93 | if( t.d_type == Tok_Eof ) 94 | { 95 | d_files.removeAll(d_stack.back().d_lex.getDevice()); 96 | delete d_stack.back().d_lex.getDevice(); 97 | d_sloc += d_stack.back().d_lex.getSloc(); 98 | d_mutes.insert(t.d_sourcePath, d_stack.back().d_mutes); 99 | d_stack.pop_back(); 100 | if( d_stack.isEmpty() ) 101 | return Token(Tok_Eof); 102 | } 103 | t = d_stack.back().d_lex.nextToken(); 104 | } 105 | while( t.d_type == Tok_INCLUDE ) 106 | { 107 | if( !handleInclude(t) ) 108 | { 109 | Token err(Tok_Invalid,t.d_lineNr,t.d_colNr,d_err.toUtf8()); 110 | err.d_sourcePath = t.d_sourcePath; 111 | return err; 112 | } 113 | t = d_stack.back().d_lex.nextToken(); 114 | } 115 | return t; 116 | } 117 | 118 | bool PpLexer::handleInclude(Token t) 119 | { 120 | const FileSystem::File* f = d_fs->findFile(t.d_sourcePath); 121 | Q_ASSERT( f ); 122 | 123 | QStringList path; 124 | int line = t.d_lineNr, startCol = 0, endCol = 0; 125 | while( t.isValid() && t.d_type != Tok_eol ) 126 | { 127 | t = d_stack.back().d_lex.nextToken(); 128 | if( t.d_type == Tok_ident ) 129 | { 130 | if( startCol == 0 ) 131 | startCol = t.d_colNr; 132 | path.append( QString::fromUtf8(t.d_val).toLower() ); 133 | endCol = t.d_val.size() + t.d_colNr; 134 | } 135 | } 136 | 137 | QString fileName = path.last(); 138 | if( fileName.endsWith(".text") ) 139 | fileName.chop(5); 140 | const FileSystem::File* found = 0; 141 | // TODO: find can still be improved 142 | if( path.size() == 1 ) 143 | { 144 | found = d_fs->findFile(f->d_dir, QString(), fileName); 145 | }else if( path.size() == 2 ) 146 | found = d_fs->findFile(f->d_dir, path[0], fileName); 147 | 148 | Include inc; 149 | inc.d_inc = found; 150 | inc.d_loc = RowCol(line,startCol); 151 | inc.d_sourcePath = t.d_sourcePath; 152 | inc.d_len = endCol - startCol; 153 | d_includes.append(inc); 154 | d_err.clear(); 155 | if( found ) 156 | { 157 | d_stack.push_back(Level()); 158 | QFile* file = new QFile(found->d_realPath); 159 | if( !file->open(QIODevice::ReadOnly) ) 160 | { 161 | delete file; 162 | d_stack.pop_back(); 163 | d_err = QString("file '%1' cannot be opened").arg(path.join('/')).toUtf8(); 164 | }else 165 | { 166 | d_stack.back().d_lex.setStream(file,found->d_realPath); 167 | d_stack.back().d_lex.setMacros(&d_macros); 168 | } 169 | }else 170 | d_err = QString("assembler include file '%1' not found").arg(path.join('/')).toUtf8(); 171 | return found; 172 | } 173 | 174 | bool PpLexer::handleSetc(const QByteArray& data) 175 | { 176 | #if 0 177 | // TODO 178 | QByteArray statement = data; 179 | QBuffer buf(&statement); 180 | buf.open(QIODevice::ReadOnly); 181 | Lexer lex; 182 | lex.setStream(&buf); 183 | Token tt = lex.nextToken(); 184 | if( tt.d_type != Tok_ident ) 185 | return error("expecting identifier on left side of SETC assignment"); 186 | const QByteArray var = tt.d_val.toLower(); 187 | if( var == "true" || var == "false" ) 188 | return error("cannot assign to true or false in SETC"); 189 | tt = lex.nextToken(); 190 | if( tt.d_type != Tok_ColonEq && tt.d_type != Tok_Eq ) 191 | return error("expecting ':=' or '=' in SETC assignment"); 192 | PpEval e(d_ppVars,lex); 193 | if( !e.eval() ) 194 | return error(QString("%1 in SETC expression").arg(e.getErr().constData())); 195 | d_ppVars[var] = e.getRes(); 196 | #endif 197 | return true; 198 | } 199 | 200 | bool PpLexer::handleIfc(const QByteArray& data) 201 | { 202 | #if 0 203 | // TODO 204 | QByteArray statement = data; 205 | QBuffer buf(&statement); 206 | buf.open(QIODevice::ReadOnly); 207 | Lexer lex; 208 | lex.setStream(&buf); 209 | PpEval e(d_ppVars,lex); 210 | if( !e.eval() ) 211 | return error(QString("%1 in SETC expression").arg(e.getErr().constData())); 212 | 213 | const bool cond = e.getRes(); 214 | d_conditionStack.append( ppstatus(false) ); 215 | ppsetthis( ppouter().open && cond ); 216 | #endif 217 | return true; 218 | } 219 | 220 | bool PpLexer::handleElsec() 221 | { 222 | if( ppthis().elseSeen || d_conditionStack.isEmpty() ) 223 | return error("ELSEC not expected here"); 224 | else 225 | ppsetthis( ppouter().open && !ppthis().openSeen, true ); 226 | return true; 227 | } 228 | 229 | bool PpLexer::handleEndc() 230 | { 231 | if( d_conditionStack.isEmpty() ) 232 | return error("spurious ENDC"); 233 | else 234 | d_conditionStack.pop_back(); 235 | return true; 236 | } 237 | 238 | bool PpLexer::error(const QString& msg) 239 | { 240 | d_err = msg; 241 | return false; 242 | } 243 | 244 | -------------------------------------------------------------------------------- /LICENSE.LGPLv3: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright © 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies of this 6 | licensedocument, but changing it is not allowed. 7 | 8 | This version of the GNU Lesser General Public License incorporates 9 | the terms and conditions of version 3 of the GNU General Public 10 | License, supplemented by the additional permissions listed below. 11 | 12 | 0. Additional Definitions. 13 | 14 | As used herein, “this License” refers to version 3 of the GNU Lesser 15 | General Public License, and the “GNU GPL” refers to version 3 of the 16 | GNU General Public License. 17 | 18 | “The Library” refers to a covered work governed by this License, 19 | other than an Application or a Combined Work as defined below. 20 | 21 | An “Application” is any work that makes use of an interface provided 22 | by the Library, but which is not otherwise based on the Library. 23 | Defining a subclass of a class defined by the Library is deemed a mode 24 | of using an interface provided by the Library. 25 | 26 | A “Combined Work” is a work produced by combining or linking an 27 | Application with the Library. The particular version of the Library 28 | with which the Combined Work was made is also called the “Linked 29 | Version”. 30 | 31 | The “Minimal Corresponding Source” for a Combined Work means the 32 | Corresponding Source for the Combined Work, excluding any source code 33 | for portions of the Combined Work that, considered in isolation, are 34 | based on the Application, and not on the Linked Version. 35 | 36 | The “Corresponding Application Code” for a Combined Work means the 37 | object code and/or source code for the Application, including any data 38 | and utility programs needed for reproducing the Combined Work from the 39 | Application, but excluding the System Libraries of the Combined Work. 40 | 41 | 1. Exception to Section 3 of the GNU GPL. 42 | 43 | You may convey a covered work under sections 3 and 4 of this License 44 | without being bound by section 3 of the GNU GPL. 45 | 46 | 2. Conveying Modified Versions. 47 | 48 | If you modify a copy of the Library, and, in your modifications, a 49 | facility refers to a function or data to be supplied by an Application 50 | that uses the facility (other than as an argument passed when the 51 | facility is invoked), then you may convey a copy of the modified 52 | version: 53 | 54 | a) under this License, provided that you make a good faith effort 55 | to ensure that, in the event an Application does not supply the 56 | function or data, the facility still operates, and performs 57 | whatever part of its purpose remains meaningful, or 58 | 59 | b) under the GNU GPL, with none of the additional permissions of 60 | this License applicable to that copy. 61 | 62 | 3. Object Code Incorporating Material from Library Header Files. 63 | 64 | The object code form of an Application may incorporate material from 65 | a header file that is part of the Library. You may convey such object 66 | code under terms of your choice, provided that, if the incorporated 67 | material is not limited to numerical parameters, data structure 68 | layouts and accessors, or small macros, inline functions and templates 69 | (ten or fewer lines in length), you do both of the following: 70 | 71 | a) Give prominent notice with each copy of the object code that 72 | the Library is used in it and that the Library and its use are 73 | covered by this License. 74 | 75 | b) Accompany the object code with a copy of the GNU GPL and this 76 | license document. 77 | 78 | 4. Combined Works. 79 | 80 | You may convey a Combined Work under terms of your choice that, taken 81 | together, effectively do not restrict modification of the portions of 82 | the Library contained in the Combined Work and reverse engineering for 83 | debugging such modifications, if you also do each of the following: 84 | 85 | a) Give prominent notice with each copy of the Combined Work that 86 | the Library is used in it and that the Library and its use are 87 | covered by this License. 88 | 89 | b) Accompany the Combined Work with a copy of the GNU GPL and this 90 | license document. 91 | 92 | c) For a Combined Work that displays copyright notices during 93 | execution, include the copyright notice for the Library among 94 | these notices, as well as a reference directing the user to the 95 | copies of the GNU GPL and this license document. 96 | 97 | d) Do one of the following: 98 | 99 | 0) Convey the Minimal Corresponding Source under the terms of 100 | this License, and the Corresponding Application Code in a form 101 | suitable for, and under terms that permit, the user to 102 | recombine or relink the Application with a modified version of 103 | the Linked Version to produce a modified Combined Work, in the 104 | manner specified by section 6 of the GNU GPL for conveying 105 | Corresponding Source. 106 | 107 | 1) Use a suitable shared library mechanism for linking with 108 | the Library. A suitable mechanism is one that (a) uses at run 109 | time a copy of the Library already present on the user's 110 | computer system, and (b) will operate properly with a modified 111 | version of the Library that is interface-compatible with the 112 | Linked Version. 113 | 114 | e) Provide Installation Information, but only if you would 115 | otherwise be required to provide such information under section 6 116 | of the GNU GPL, and only to the extent that such information is 117 | necessary to install and execute a modified version of the 118 | Combined Work produced by recombining or relinking the Application 119 | with a modified version of the Linked Version. (If you use option 120 | 4d0, the Installation Information must accompany the Minimal 121 | Corresponding Source and Corresponding Application Code. If you 122 | use option 4d1, you must provide the Installation Information in 123 | the manner specified by section 6 of the GNU GPL for conveying 124 | Corresponding Source.) 125 | 126 | 5. Combined Libraries. 127 | 128 | You may place library facilities that are a work based on the Library 129 | side by side in a single library together with other library 130 | facilities that are not Applications and are not covered by this 131 | License, and convey such a combined library under terms of your 132 | choice, if you do both of the following: 133 | 134 | a) Accompany the combined library with a copy of the same work 135 | based on the Library, uncombined with any other library 136 | facilities, conveyed under the terms of this License. 137 | 138 | b) Give prominent notice with the combined library that part of 139 | it is a work based on the Library, and explaining where to find 140 | the accompanying uncombined form of the same work. 141 | 142 | 6. Revised Versions of the GNU Lesser General Public License. 143 | 144 | The Free Software Foundation may publish revised and/or new versions 145 | of the GNU Lesser General Public License from time to time. Such new 146 | versions will be similar in spirit to the present version, but may 147 | differ in detail to address new problems or concerns. 148 | 149 | Each version is given a distinguishing version number. If the Library 150 | as you received it specifies that a certain numbered version of the 151 | GNU Lesser General Public License “or any later version” applies to 152 | it, you have the option of following the terms and conditions either 153 | of that published version or of any later version published by the 154 | Free Software Foundation. If the Library as you received it does not 155 | specify a version number of the GNU Lesser General Public License, 156 | you may choose any version of the GNU Lesser General Public License 157 | ever published by the Free Software Foundation. 158 | 159 | If the Library as you received it specifies that a proxy can decide 160 | whether future versions of the GNU Lesser General Public License shall 161 | apply, that proxy's public statement of acceptance of any version is 162 | permanent authorization for you to choose that version for the Library. 163 | 164 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | This is a parser and code navigator for the Lisa Pascal dialect and associated assembler which I started to implement on January 22, 2023. The reason was that the Computer History Museum published the Apple Lisa source code on January 19, 2023 (see https://computerhistory.org/blog/the-lisa-apples-most-influential-failure/), and I was looking for a tool to study/analyze the code. 2 | 3 | Lisa Pascal was an extension to the Pascal used on the Apple II and III, but not yet object-oriented. I found a [specification of the language here](http://lisa.sunder.net/Pascal_Reference.pdf) which I used to implement the parser. During the project it turned out that also the Lisa OS source tree includes Clascal (~5%), i.e. not only the Lisa Toolkit, and that also a preprocessor had to be implemented to understand the code. 4 | 5 | To develop and debug the grammar I used my [EbnfStudio tool](https://github.com/rochus-keller/EbnfStudio). I started based on a Pascal grammar corresponding to the 1975 version of Wirth and Jensen's "Pascal User Manual & Report" which I found [here](https://www.icosaedro.it/bnf_chk/ebnf-pascal.txt). I then modified this grammar for EbnfStudio compatibility, fixed left recursion and LL(1) ambiguity, and then correlated the grammar with the mentioned "Pascal Reference Manual for the Lisa", which lead to quite a few additions, removals or other changes. After writing a lexer and generating a parser using Coco/R (based on the grammar file generated by EbnfStudio) I adapted the grammar to the actual syntax used in the Lisa source files. 6 | 7 | The Lisa Source code provided by the Computer History Museum contains 1092 plain text source files; further analysis revealed that 614 of which are Pascal and ~203 are MC68000 assembler files; another 126 files are EXEC scripts, and the remaining 144 are emails, documentation, protocols and text resources. About 120 files look like binary encoded sources and other texts which I didn't inspect yet. 8 | 9 | Cloc (http://cloc.sourceforge.net) counts 456 kSLOC, of which 408 kSLOC are Pascal, 45 kSLOC are Assembler and 4 kSLOC are EXEC scripts. 10 | 11 | #### Lisa Code Navigator 12 | 13 | ![LisaCodeNavigator Screenshot](http://software.rochus-keller.ch/lisacodenavigator-0.6.3-screenshot.png) 14 | 15 | ![LisaCodeNavigator Screenshot](http://software.rochus-keller.ch/lisacodenavigator-0.8.1-screenshot.png) 16 | 17 | #### Planned features 18 | 19 | My primary goal was to implement tools as I did it e.g. for [Oberon](https://github.com/rochus-keller/Oberon/blob/master/README_old.md) or [Smalltalk](https://github.com/rochus-keller/Smalltalk/blob/master/Readme.md) to analyze the Lisa source code and check it e.g. for completeness and feasibility to compile and run it on a Lisa emulator (such as https://lisa.sunder.net/). 20 | 21 | - [x] Lisa Pascal and Clascal parser, adapted to the source code at hand. 22 | - [x] Preprocessor to include files and employ conditional code directives 23 | - [x] Overlay file system to accomodate original structure and resolve dependencies 24 | - [x] Highlighted code browser 25 | - [x] Mark symbols and navigate from symbols do declarations across all files 26 | - [x] BUSY & LeanQt build 27 | - [x] resolve qualifiers by type for navigation of record fields 28 | - [x] precompiled binary versions for main platforms 29 | - [x] Module detail outline view 30 | - [x] MC68000 assembler integrated with code model and symbol navigation 31 | 32 | #### Features in evaluation 33 | 34 | - [ ] Class hierarchy outline view 35 | - [ ] transpiler to Free Pascal for MC68000 code generation 36 | - [ ] run the code on a Lisa emulator 37 | 38 | #### Status on January 23, 2023 39 | 40 | The parser works in principle; 381 of the 614 Lisa Pascal file found in the source tree parse without an error; most of the other 233 Lisa Pascal files have the same parser error, which is caused because the files are either incomplete or there are alternative parts of the code in the same file. It turned out that the Lisa source code heavily depends on a preprocessor, which is controled by source code directives of the form {$directive arguments}. There is e.g a {$I filepath} directive to include other source files, or an {$IFC variable} directive to enable or mute sections of the file (like #ifdef/#endif in C). Without this preprocessor, which I have not implemented (yet), the mentioned syntax errors cannot be avoided. I e.g. had to add a "non_regular_unit" to the grammar to deal with the fact, that some files only include a part of a program or a unit, so the parser could handle these files; but this is only a provisional fix and to continue the project the implementation of the preprocessor is unavoidable. 41 | 42 | #### Status on January 26, 2023 43 | 44 | The source tree is converted to a virtual file system using the original file names, considering Pascal files only. All program and unit files 45 | of this file system are parsed and include directives are resolved. Of the 360 program or unit files (with includes) 199 parse without an error. 46 | The Lisa toolkit uses Clascal which is not yet supported and leads to parsing errors. 47 | 48 | #### Status on January 28, 2023 49 | 50 | Conditional compilation and the Clascal syntax are implemented and tested. The LISA_OS part of the tree (about 200 kSLOC) parses with no errors but four missing include files. The full source tree (about 400 kSLOC measured with my tools) has 12 missing include files and 7 syntax errors. The 400 kSLOC parse on my machine in less than 7 seconds. 51 | 52 | The CodeNavigator shows the overlay file system (i.e. the one assumed by the source files) with all programs and units; the files can be browsed in a syntax highlighted viewer; semantic navigation is pending. 53 | 54 | #### Status on January 29, 2023 55 | 56 | An AST (as far as required) is constructed from the syntax and symbols are resolved (including imported modules) for crossreferencing. Source code navigation works (e.g. press Ctrl key while moving the cursor over idents and click to navigate to declaration if an underline is visible), but not all idents are indexed yet. 57 | Symbol lookup is too slow in the current implementation; I will do it as I did in my Oberon+ IDE with pointer instead of string comparisons, which requires an extra copy of the ident in lower case; the analysis of the about 400 kSLOC currently requires 11 seconds (instead of 7) on my machine. 58 | 59 | #### Status on January 30, 2023 60 | 61 | After a lot of debugging and fixing source code navigation works as expected (besides some symbols not yet indexed), and also the "Used by" cross-reference list is implemented and can be used for navigation (double-click). Also navigation history is improved (ALT+Left, ALT+Right) und synced with the views. 62 | The indexer now considers the full syntax, including modules and imports, but qualifiers are not yet resolved. 63 | I also implemented internalized strings with comparisons by address instead of by string, and now indexing takes only ~8% more time than just parsing. 64 | There is now also a BUSY build file, see below how to use it. 65 | 66 | #### Status on February 8, 2023 67 | 68 | The MC68000 assembly language parser is implemented and integrated; Pascal procedures marked as external look for assembler implementations in the same virtual directory; the majority of names can be resolved that way (776 vs 534). I also implemented some convenience features like grayed-out sections in Pascal, sizable browser font, marks for unresoved symbols, and more Clascal support. With this release, all features planned so far have been implemented. 69 | 70 | #### Precompiled versions 71 | 72 | The following precompiled versions are available at this time: 73 | 74 | - [Windows x86](http://software.rochus-keller.ch/LisaCodeNavigator_win32.zip) 75 | - [Linux x86_64](http://software.rochus-keller.ch/LisaCodeNavigator_linux64.tar.gz) 76 | - [Mac x86_64](http://software.rochus-keller.ch/LisaCodeNavigator_mac64.zip) 77 | 78 | 79 | #### How to build the parser and code navigator 80 | 81 | The executable can be built on all common platforms using regular Qt 5.x or using [LeanQt](https://github.com/rochus-keller/LeanQt) with minimal dependencies. 82 | 83 | The project includes the CodeNavigator.pro file which can be opened and built in Qt Creator or directly with qmake on the command line. 84 | 85 | To build the Code Navigator using LeanQt and the BUSY build system (with no other dependencies than a C++98 compiler) instead, do the following: 86 | 87 | 1. Create a new directory; we call it the root directory here 88 | 1. Download https://github.com/rochus-keller/LisaPascal/archive/refs/heads/master.zip and unpack it to the root directory; rename the resulting directory to "LisaPascal". 89 | 1. Download https://github.com/rochus-keller/LeanQt/archive/refs/heads/master.zip and unpack it to the root directory; rename the resulting directory to "LeanQt". 90 | 1. Download https://github.com/rochus-keller/BUSY/archive/refs/heads/master.zip and unpack it to the root directory; rename the resulting directory to "build". 91 | 1. Open a command line in the build directory and type `cc *.c -O2 -lm -O2 -o lua` or `cl /O2 /MD /Fe:lua.exe *.c` depending on whether you are on a Unix or Windows machine; wait a few seconds until the Lua executable is built. 92 | 1. Now type `./lua build.lua ../LisaPascal` (or `lua build.lua ../LisaPascal` on Windows); wait until the LisaCodeNavigator executable is built; you find it in the output subdirectory. 93 | 94 | #### Support 95 | If you need support or would like to post issues or feature requests please use the Github issue list at https://github.com/rochus-keller/LisaPascal/issues or send an email to the author. 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /LisaCodeModel.h: -------------------------------------------------------------------------------- 1 | #ifndef LISACODEMODEL_H 2 | #define LISACODEMODEL_H 3 | 4 | /* 5 | * Copyright 2023 Rochus Keller 6 | * 7 | * This file is part of the Lisa Pascal Navigator application. 8 | * 9 | * The following is the license that applies to this copy of the 10 | * application. For a license to use the library under conditions 11 | * other than those described here, please email to me@rochus-keller.ch. 12 | * 13 | * GNU General Public License Usage 14 | * This file may be used under the terms of the GNU General Public 15 | * License (GPL) versions 2.0 or 3.0 as published by the Free Software 16 | * Foundation and appearing in the file LICENSE.GPL included in 17 | * the packaging of this file. Please review the following information 18 | * to ensure GNU General Public Licensing requirements will be met: 19 | * http://www.fsf.org/licensing/licenses/info/GPLv2.html and 20 | * http://www.gnu.org/copyleft/gpl.html. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "LisaRowCol.h" 28 | 29 | namespace Lisa 30 | { 31 | class Scope; 32 | class UnitFile; 33 | class Symbol; 34 | 35 | class Thing 36 | { 37 | public: 38 | enum Kind { Undefined, 39 | /* Declaration: */ Const, TypeDecl, Var, Func, Proc, AsmDef, MethBlock, Label, Param, Field, 40 | Module, AsmRef, AsmMacro, Self, 41 | /* Scope: */ Interface, Implementation, Body, Members, 42 | /* UnitFile: */ Unit, 43 | /* AsmFile: */ Assembler, 44 | /* AsmInclude: */ AsmIncl, 45 | /* IncludeFile: */ Include, 46 | /* CodeFolder: */ Folder 47 | }; 48 | quint8 d_kind; 49 | 50 | virtual FilePos getLoc() const { return FilePos(); } 51 | virtual quint16 getLen() const { return 0; } 52 | virtual QString getName() const; 53 | virtual const FileSystem::File* getFile() const { return 0; } 54 | bool isDeclaration() const { return d_kind >= Const && d_kind <= Self; } 55 | const char* typeName() const; 56 | Thing():d_kind(Undefined){} 57 | virtual ~Thing(); 58 | }; 59 | 60 | class Type : public QSharedData 61 | { 62 | public: 63 | typedef QExplicitlySharedDataPointer Ref; 64 | enum Kind { Undefined, Pointer, Array, Record, Class 65 | }; 66 | Type::Ref d_type; 67 | Scope* d_members; // owns 68 | quint8 d_kind; 69 | 70 | Type():d_type(0), d_members(0),d_kind(Undefined) {} 71 | ~Type(); 72 | }; 73 | 74 | class Declaration : public Thing 75 | { 76 | public: 77 | Scope* d_body; // owns 78 | Type::Ref d_type; 79 | QByteArray d_name; 80 | const char* d_id; // same as in Token 81 | 82 | FilePos d_loc; // place in unit or any include file where the decl is actually located 83 | Scope* d_owner; 84 | 85 | typedef QList SymList; 86 | typedef QHash Refs; 87 | Refs d_refs; // file path -> Symbols in it 88 | Symbol* d_me; // this is the symbol by which the decl itself is represented in the file 89 | Declaration* d_impl; // points to implementation if this is in an interface or a forward 90 | 91 | FilePos getLoc() const { return d_loc; } 92 | quint16 getLen() const { return d_name.size(); } 93 | QString getName() const; 94 | 95 | UnitFile* getUnitFile() const; // only for ownership, not for actual file position 96 | Declaration():d_body(0),d_owner(0),d_me(0),d_id(0),d_type(0),d_impl(0){} 97 | ~Declaration(); 98 | }; 99 | 100 | class Scope : public Thing 101 | { 102 | public: 103 | QList d_order; // owns 104 | Thing* d_owner; // either declaration or unit file or asm file or 0 105 | Scope* d_outer; 106 | Scope* d_altOuter; // to access params defined in interface declaration of func/proc 107 | 108 | UnitFile* getUnitFile() const; 109 | Declaration* findDecl(const char* id, bool withImports = true) const; 110 | void clear(); 111 | Scope():d_owner(0),d_outer(0),d_altOuter(0){} 112 | ~Scope(); 113 | }; 114 | 115 | class Symbol 116 | { 117 | public: 118 | Thing* d_decl; 119 | RowCol d_loc; // the position of the symbol in the file (declaration has other position, but same length) 120 | Symbol():d_decl(0){} 121 | }; 122 | 123 | class IncludeFile; 124 | class UnitFile; 125 | class AsmFile; 126 | class AsmInclude; 127 | class CodeFolder; 128 | 129 | class CodeFile : public Thing 130 | { 131 | public: 132 | const FileSystem::File* d_file; 133 | CodeFolder* d_folder; 134 | 135 | UnitFile* toUnit(); 136 | IncludeFile* toInclude(); 137 | AsmFile* toAsmFile(); 138 | AsmInclude* toAsmInclude(); 139 | 140 | QString getName() const; 141 | const FileSystem::File* getFile() const { return d_file; } 142 | CodeFile():d_file(0),d_folder(0) {} 143 | ~CodeFile(); 144 | }; 145 | 146 | class IncludeFile : public CodeFile 147 | { 148 | public: 149 | UnitFile* d_unit; 150 | quint16 d_len; // just to make the symbol of the include directive happy 151 | 152 | FilePos getLoc() const; // the landing place when we jump to this file 153 | quint16 getLen() const { return d_len; } 154 | IncludeFile():d_unit(0),d_len(0){ d_kind = Include; } 155 | }; 156 | 157 | class UnitFile : public CodeFile 158 | { 159 | public: 160 | Scope* d_intf; // owns, 0 for Program 161 | Scope* d_impl; // owns 162 | Scope* d_globals; 163 | QList d_import; 164 | typedef QList SymList; 165 | QHash d_syms; // owns, all things we can click on in a code file ordered by row/col 166 | QList d_includes; // owns 167 | 168 | QByteArrayList findUses() const; 169 | UnitFile():d_intf(0),d_impl(0),d_globals(0) { d_kind = Unit; } 170 | ~UnitFile(); 171 | }; 172 | 173 | class AsmInclude : public CodeFile 174 | { 175 | public: 176 | quint16 d_len; // just to make the symbol of the include directive happy 177 | quint16 d_col; 178 | quint32 d_row; 179 | AsmFile* d_unit; 180 | 181 | FilePos getLoc() const; // the landing place when we jump to this file 182 | quint16 getLen() const { return d_len; } 183 | AsmInclude():d_len(0),d_col(0),d_row(0),d_unit(0){ d_kind = AsmIncl; } 184 | }; 185 | 186 | class AsmFile : public CodeFile 187 | { 188 | public: 189 | Scope* d_impl; // owns 190 | QList d_includes; // owns 191 | typedef QList SymList; 192 | QHash d_syms; // owns, all things we can click on in a code file ordered by row/col 193 | 194 | AsmFile():d_impl(0){ d_kind = Assembler; } 195 | ~AsmFile(); 196 | }; 197 | 198 | class CodeFolder : public Thing 199 | { 200 | public: 201 | FileSystem::Dir* d_dir; 202 | QList d_subs; // owns 203 | QList d_files; // owns 204 | 205 | QString getName() const; 206 | void clear(); 207 | CodeFolder():d_dir(0){ d_kind = Folder; } 208 | ~CodeFolder() { clear(); } 209 | }; 210 | 211 | struct ModelItem 212 | { 213 | Thing* d_thing; 214 | QList d_children; 215 | ModelItem* d_parent; 216 | ModelItem(ModelItem* p = 0, Thing* t = 0):d_parent(p),d_thing(t){ if( p ) p->d_children.append(this); } 217 | ~ModelItem() { foreach( ModelItem* s, d_children ) delete s; } 218 | static bool lessThan( const ModelItem* lhs, const ModelItem* rhs); 219 | }; 220 | 221 | class ItemModel : public QAbstractItemModel 222 | { 223 | Q_OBJECT 224 | public: 225 | explicit ItemModel(QObject *parent = 0); 226 | const Thing* getThing(const QModelIndex& index) const; 227 | QModelIndex findThing(const Thing* nt) const; 228 | 229 | // overrides 230 | int columnCount ( const QModelIndex & parent = QModelIndex() ) const { return 1; } 231 | QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; 232 | QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const; 233 | QModelIndex parent ( const QModelIndex & index ) const; 234 | int rowCount ( const QModelIndex & parent = QModelIndex() ) const; 235 | Qt::ItemFlags flags ( const QModelIndex & index ) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } 236 | protected: 237 | QModelIndex findThing(const ModelItem* slot,const Thing* nt) const; 238 | ModelItem d_root; 239 | }; 240 | 241 | class CodeModel : public ItemModel 242 | { 243 | Q_OBJECT 244 | public: 245 | explicit CodeModel(QObject *parent = 0); 246 | 247 | bool load( const QString& rootDir ); 248 | Symbol* findSymbolBySourcePos(const QString& path, int line, int col) const; 249 | FileSystem* getFs() const { return d_fs; } 250 | quint32 getSloc() const { return d_sloc; } 251 | CodeFile* getCodeFile(const QString& path) const; 252 | UnitFile* getUnitFile(const QString& path) const; 253 | AsmFile* getAsmFile(const QString& path) const; 254 | Scope* getGlobals() { return &d_globals; } 255 | Ranges getMutes( const QString& path ); 256 | int getErrCount() const { return d_errCount; } 257 | 258 | // overrides 259 | QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; 260 | protected: 261 | void parseAndResolve(UnitFile*); 262 | void parseAndResolve(AsmFile*); 263 | 264 | private: 265 | void fillFolders(ModelItem* root, const FileSystem::Dir* super, CodeFolder* top, QList& fileSlots); 266 | FileSystem* d_fs; 267 | CodeFolder d_top; 268 | Scope d_globals; 269 | QHash d_map1; 270 | QHash d_map2; // real path -> file 271 | quint32 d_sloc; // number of lines of code without empty or comment lines 272 | QHash d_mutes; 273 | int d_errCount; 274 | }; 275 | 276 | class ModuleDetailMdl : public ItemModel 277 | { 278 | Q_OBJECT 279 | public: 280 | explicit ModuleDetailMdl(QObject *parent = 0); 281 | 282 | void load(Scope* intf, Scope* impl); 283 | 284 | // overrides 285 | QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; 286 | private: 287 | void fillItems(ModelItem* parentItem, Scope* scope); 288 | void fillSubs(ModelItem* parentItem, Scope* scope); 289 | Scope* d_intf; 290 | Scope* d_impl; 291 | }; 292 | 293 | } 294 | 295 | #endif // LISACODEMODEL_H 296 | -------------------------------------------------------------------------------- /asm/Parser.frame: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | Compiler Generator Coco/R, 3 | Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz 4 | extended by M. Loeberbauer & A. Woess, Univ. of Linz 5 | ported to C++ by Csaba Balazs, University of Szeged 6 | with improvements by Pat Terry, Rhodes University 7 | modified by Rochus Keller, me@rochus-keller.ch, (c) 2019 8 | 9 | This program is free software; you can redistribute it and/or modify it 10 | under the terms of the GNU General Public License as published by the 11 | Free Software Foundation; either version 2, or (at your option) any 12 | later version. 13 | 14 | This program is distributed in the hope that it will be useful, but 15 | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 | for more details. 18 | 19 | You should have received a copy of the GNU General Public License along 20 | with this program; if not, write to the Free Software Foundation, Inc., 21 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 | 23 | As an exception, it is allowed to write an extension of Coco/R that is 24 | used as a plugin in non-free software. 25 | 26 | If not otherwise stated, any source code generated by Coco/R (other than 27 | Coco/R itself) does not fall under the GNU General Public License. 28 | -------------------------------------------------------------------------*/ 29 | 30 | /*---------------------------------------------------------------------- 31 | Parser.h Specification 32 | -----------------------------------------------------------------------*/ 33 | 34 | -->begin 35 | 36 | // This file was automatically generated by Coco/R; don't modify it. 37 | #if !defined(-->prefixCOCO_PARSER_H__) 38 | #define -->prefixCOCO_PARSER_H__ 39 | 40 | -->headerdef 41 | 42 | -->namespace_open 43 | 44 | class PpLexer; 45 | class Parser { 46 | private: 47 | -->constantsheader 48 | int errDist; 49 | int minErrDist; 50 | 51 | void SynErr(int n, const char* ctx = 0); 52 | void Get(); 53 | void Expect(int n, const char* ctx = 0); 54 | bool StartOf(int s); 55 | void ExpectWeak(int n, int follow); 56 | bool WeakSeparator(int n, int syFol, int repFol); 57 | void SynErr(int line, int col, int n, const char* ctx, const QString&, const QString& path ); 58 | 59 | public: 60 | PpLexer *scanner; 61 | struct Error 62 | { 63 | QString msg; 64 | int row, col; 65 | QString path; 66 | }; 67 | QList errors; 68 | 69 | void error(int row, int col, const QString& msg, const QString& path) 70 | { 71 | Error e; 72 | e.row = row; 73 | e.col = col; 74 | e.msg = msg; 75 | e.path = path; 76 | errors.append(e); 77 | } 78 | 79 | Token d_cur; 80 | Token d_next; 81 | QList d_comments; 82 | struct TokDummy 83 | { 84 | int kind; 85 | }; 86 | TokDummy d_dummy; 87 | TokDummy *la; // lookahead token 88 | 89 | int peek( quint8 la = 1 ); 90 | 91 | void RunParser(); 92 | 93 | 94 | -->declarations 95 | 96 | Parser(PpLexer *scanner); 97 | ~Parser(); 98 | void SemErr(const char* msg); 99 | 100 | -->productionsheader 101 | void Parse(); 102 | 103 | }; // end Parser 104 | 105 | -->namespace_close 106 | 107 | #endif 108 | 109 | -->implementation 110 | 111 | /*---------------------------------------------------------------------- 112 | Parser.cpp Specification 113 | -----------------------------------------------------------------------*/ 114 | 115 | -->begin 116 | 117 | // This file was automatically generated by Coco/R; don't modify it. 118 | #include "AsmParser.h" 119 | #include "AsmPpLexer.h" 120 | 121 | 122 | -->namespace_open 123 | 124 | static QString coco_string_create( const wchar_t* str ) 125 | { 126 | return QString::fromStdWString(str); 127 | } 128 | 129 | int Parser::peek( quint8 la ) 130 | { 131 | if( la == 0 ) 132 | return d_cur.d_type; 133 | else if( la == 1 ) 134 | return d_next.d_type; 135 | else 136 | return scanner->peekToken( la - 1 ).d_type; 137 | } 138 | 139 | void Parser::RunParser() 140 | { 141 | d_stack.push(&d_root); 142 | Parse(); 143 | d_stack.pop(); 144 | } 145 | 146 | void Parser::SynErr(int n, const char* ctx) { 147 | if (errDist >= minErrDist) 148 | SynErr(d_next.d_lineNr, d_next.d_colNr, n, ctx, QString(), d_next.d_sourcePath); 149 | errDist = 0; 150 | } 151 | 152 | void Parser::SemErr(const char* msg) { 153 | if (errDist >= minErrDist) 154 | error(d_cur.d_lineNr, d_cur.d_colNr, msg, d_cur.d_sourcePath); 155 | errDist = 0; 156 | } 157 | 158 | void Parser::Get() { 159 | for (;;) { 160 | d_cur = d_next; 161 | d_next = scanner->nextToken(); 162 | bool deliverToParser = false; 163 | switch( d_next.d_type ) 164 | { 165 | case Tok_Invalid: 166 | if( !d_next.d_val.isEmpty() ) 167 | error( d_next.d_lineNr, d_next.d_colNr, d_next.d_val, d_next.d_sourcePath ); 168 | // else errors already handeled in lexer 169 | break; 170 | case Tok_Comment: 171 | d_comments.append(d_next); 172 | break; 173 | default: 174 | deliverToParser = true; 175 | break; 176 | } 177 | 178 | if( deliverToParser ) 179 | { 180 | if( d_next.d_type == Tok_Eof ) 181 | d_next.d_type = _EOF; 182 | 183 | la->kind = d_next.d_type; 184 | if (la->kind <= maxT) 185 | { 186 | ++errDist; 187 | break; 188 | } 189 | } 190 | -->pragmas 191 | d_next = d_cur; 192 | } 193 | } 194 | 195 | void Parser::Expect(int n, const char* ctx ) { 196 | if (la->kind==n) Get(); else { SynErr(n, ctx); } 197 | } 198 | 199 | void Parser::ExpectWeak(int n, int follow) { 200 | if (la->kind == n) Get(); 201 | else { 202 | SynErr(n); 203 | while (!StartOf(follow)) Get(); 204 | } 205 | } 206 | 207 | bool Parser::WeakSeparator(int n, int syFol, int repFol) { 208 | if (la->kind == n) {Get(); return true;} 209 | else if (StartOf(repFol)) {return false;} 210 | else { 211 | SynErr(n); 212 | while (!(StartOf(syFol) || StartOf(repFol) || StartOf(0))) { 213 | Get(); 214 | } 215 | return StartOf(syFol); 216 | } 217 | } 218 | 219 | -->productions 220 | 221 | 222 | // If the user declared a method Init and a mehtod Destroy they should 223 | // be called in the contructur and the destructor respctively. 224 | // 225 | // The following templates are used to recognize if the user declared 226 | // the methods Init and Destroy. 227 | 228 | template 229 | struct ParserInitExistsRecognizer { 230 | template 231 | struct ExistsIfInitIsDefinedMarker{}; 232 | 233 | struct InitIsMissingType { 234 | char dummy1; 235 | }; 236 | 237 | struct InitExistsType { 238 | char dummy1; char dummy2; 239 | }; 240 | 241 | // exists always 242 | template 243 | static InitIsMissingType is_here(...); 244 | 245 | // exist only if ExistsIfInitIsDefinedMarker is defined 246 | template 247 | static InitExistsType is_here(ExistsIfInitIsDefinedMarker*); 248 | 249 | enum { InitExists = (sizeof(is_here(NULL)) == sizeof(InitExistsType)) }; 250 | }; 251 | 252 | template 253 | struct ParserDestroyExistsRecognizer { 254 | template 255 | struct ExistsIfDestroyIsDefinedMarker{}; 256 | 257 | struct DestroyIsMissingType { 258 | char dummy1; 259 | }; 260 | 261 | struct DestroyExistsType { 262 | char dummy1; char dummy2; 263 | }; 264 | 265 | // exists always 266 | template 267 | static DestroyIsMissingType is_here(...); 268 | 269 | // exist only if ExistsIfDestroyIsDefinedMarker is defined 270 | template 271 | static DestroyExistsType is_here(ExistsIfDestroyIsDefinedMarker*); 272 | 273 | enum { DestroyExists = (sizeof(is_here(NULL)) == sizeof(DestroyExistsType)) }; 274 | }; 275 | 276 | // The folloing templates are used to call the Init and Destroy methods if they exist. 277 | 278 | // Generic case of the ParserInitCaller, gets used if the Init method is missing 279 | template::InitExists> 280 | struct ParserInitCaller { 281 | static void CallInit(T *t) { 282 | // nothing to do 283 | } 284 | }; 285 | 286 | // True case of the ParserInitCaller, gets used if the Init method exists 287 | template 288 | struct ParserInitCaller { 289 | static void CallInit(T *t) { 290 | t->Init(); 291 | } 292 | }; 293 | 294 | // Generic case of the ParserDestroyCaller, gets used if the Destroy method is missing 295 | template::DestroyExists> 296 | struct ParserDestroyCaller { 297 | static void CallDestroy(T *t) { 298 | // nothing to do 299 | } 300 | }; 301 | 302 | // True case of the ParserDestroyCaller, gets used if the Destroy method exists 303 | template 304 | struct ParserDestroyCaller { 305 | static void CallDestroy(T *t) { 306 | t->Destroy(); 307 | } 308 | }; 309 | 310 | void Parser::Parse() { 311 | d_cur = Token(); 312 | d_next = Token(); 313 | Get(); 314 | -->parseRoot 315 | } 316 | 317 | Parser::Parser(PpLexer *scanner) { 318 | -->constants 319 | ParserInitCaller::CallInit(this); 320 | la = &d_dummy; 321 | minErrDist = 2; 322 | errDist = minErrDist; 323 | this->scanner = scanner; 324 | } 325 | 326 | bool Parser::StartOf(int s) { 327 | const bool T = true; 328 | const bool x = false; 329 | 330 | -->initialization 331 | 332 | return set[s][la->kind]; 333 | } 334 | 335 | Parser::~Parser() { 336 | ParserDestroyCaller::CallDestroy(this); 337 | } 338 | 339 | void Parser::SynErr(int line, int col, int n, const char* ctx, const QString& str, const QString& path ) { 340 | QString s; 341 | QString ctxStr; 342 | if( ctx ) 343 | ctxStr = QString( " in %1" ).arg(ctx); 344 | if( n == 0 ) 345 | s = QString("EOF expected%1").arg(ctxStr); 346 | else if( n < TT_Specials ) 347 | s = QString("'%2' expected%1").arg(ctxStr).arg(tokenTypeString(n)); 348 | else if( n <= TT_Max ) 349 | s = QString("%2 expected%1").arg(ctxStr).arg(tokenTypeString(n)); 350 | else 351 | switch (n) { 352 | -->errors 353 | default: 354 | { 355 | s = QString( "generic error %1").arg(n); 356 | } 357 | break; 358 | } 359 | if( !str.isEmpty() ) 360 | s = QString("%1 %2").arg(s).arg(str); 361 | error(line, col, s, path); 362 | //count++; 363 | } 364 | 365 | -->namespace_close 366 | -------------------------------------------------------------------------------- /syntax/Parser.frame: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------- 2 | Compiler Generator Coco/R, 3 | Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz 4 | extended by M. Loeberbauer & A. Woess, Univ. of Linz 5 | ported to C++ by Csaba Balazs, University of Szeged 6 | with improvements by Pat Terry, Rhodes University 7 | modified by Rochus Keller, me@rochus-keller.ch, (c) 2019 8 | 9 | This program is free software; you can redistribute it and/or modify it 10 | under the terms of the GNU General Public License as published by the 11 | Free Software Foundation; either version 2, or (at your option) any 12 | later version. 13 | 14 | This program is distributed in the hope that it will be useful, but 15 | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17 | for more details. 18 | 19 | You should have received a copy of the GNU General Public License along 20 | with this program; if not, write to the Free Software Foundation, Inc., 21 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 | 23 | As an exception, it is allowed to write an extension of Coco/R that is 24 | used as a plugin in non-free software. 25 | 26 | If not otherwise stated, any source code generated by Coco/R (other than 27 | Coco/R itself) does not fall under the GNU General Public License. 28 | -------------------------------------------------------------------------*/ 29 | 30 | /*---------------------------------------------------------------------- 31 | Parser.h Specification 32 | -----------------------------------------------------------------------*/ 33 | 34 | -->begin 35 | 36 | // This file was automatically generated by Coco/R; don't modify it. 37 | #if !defined(-->prefixCOCO_PARSER_H__) 38 | #define -->prefixCOCO_PARSER_H__ 39 | 40 | -->headerdef 41 | 42 | -->namespace_open 43 | 44 | class PpLexer; 45 | class Parser { 46 | private: 47 | -->constantsheader 48 | int errDist; 49 | int minErrDist; 50 | 51 | void SynErr(int n, const char* ctx = 0); 52 | void Get(); 53 | void Expect(int n, const char* ctx = 0); 54 | bool StartOf(int s); 55 | void ExpectWeak(int n, int follow); 56 | bool WeakSeparator(int n, int syFol, int repFol); 57 | void SynErr(int line, int col, int n, const char* ctx, const QString&, const QString& path ); 58 | 59 | public: 60 | PpLexer *scanner; 61 | struct Error 62 | { 63 | QString msg; 64 | int row, col; 65 | QString path; 66 | }; 67 | QList errors; 68 | 69 | void error(int row, int col, const QString& msg, const QString& path) 70 | { 71 | Error e; 72 | e.row = row; 73 | e.col = col; 74 | e.msg = msg; 75 | e.path = path; 76 | errors.append(e); 77 | } 78 | 79 | Token d_cur; 80 | Token d_next; 81 | QList d_comments; 82 | struct TokDummy 83 | { 84 | int kind; 85 | }; 86 | TokDummy d_dummy; 87 | TokDummy *la; // lookahead token 88 | 89 | int peek( quint8 la = 1 ); 90 | 91 | void RunParser(); 92 | 93 | 94 | -->declarations 95 | 96 | Parser(PpLexer *scanner); 97 | ~Parser(); 98 | void SemErr(const char* msg); 99 | 100 | -->productionsheader 101 | void Parse(); 102 | 103 | }; // end Parser 104 | 105 | -->namespace_close 106 | 107 | #endif 108 | 109 | -->implementation 110 | 111 | /*---------------------------------------------------------------------- 112 | Parser.cpp Specification 113 | -----------------------------------------------------------------------*/ 114 | 115 | -->begin 116 | 117 | // This file was automatically generated by Coco/R; don't modify it. 118 | #include "LisaParser.h" 119 | #include "LisaPpLexer.h" 120 | 121 | 122 | -->namespace_open 123 | 124 | static QString coco_string_create( const wchar_t* str ) 125 | { 126 | return QString::fromStdWString(str); 127 | } 128 | 129 | int Parser::peek( quint8 la ) 130 | { 131 | if( la == 0 ) 132 | return d_cur.d_type; 133 | else if( la == 1 ) 134 | return d_next.d_type; 135 | else 136 | return scanner->peekToken( la - 1 ).d_type; 137 | } 138 | 139 | void Parser::RunParser() 140 | { 141 | d_stack.push(&d_root); 142 | Parse(); 143 | d_stack.pop(); 144 | } 145 | 146 | void Parser::SynErr(int n, const char* ctx) { 147 | if (errDist >= minErrDist) 148 | SynErr(d_next.d_lineNr, d_next.d_colNr, n, ctx, QString(), d_next.d_sourcePath); 149 | errDist = 0; 150 | } 151 | 152 | void Parser::SemErr(const char* msg) { 153 | if (errDist >= minErrDist) 154 | error(d_cur.d_lineNr, d_cur.d_colNr, msg, d_cur.d_sourcePath); 155 | errDist = 0; 156 | } 157 | 158 | void Parser::Get() { 159 | for (;;) { 160 | d_cur = d_next; 161 | d_next = scanner->nextToken(); 162 | bool deliverToParser = false; 163 | switch( d_next.d_type ) 164 | { 165 | case Lisa::Tok_Invalid: 166 | if( !d_next.d_val.isEmpty() ) 167 | error( d_next.d_lineNr, d_next.d_colNr, d_next.d_val, d_next.d_sourcePath ); 168 | // else errors already handeled in lexer 169 | break; 170 | case Lisa::Tok_Comment: 171 | d_comments.append(d_next); 172 | break; 173 | default: 174 | deliverToParser = true; 175 | break; 176 | } 177 | 178 | if( deliverToParser ) 179 | { 180 | if( d_next.d_type == Lisa::Tok_Eof ) 181 | d_next.d_type = _EOF; 182 | 183 | la->kind = d_next.d_type; 184 | if (la->kind <= maxT) 185 | { 186 | ++errDist; 187 | break; 188 | } 189 | } 190 | -->pragmas 191 | d_next = d_cur; 192 | } 193 | } 194 | 195 | void Parser::Expect(int n, const char* ctx ) { 196 | if (la->kind==n) Get(); else { SynErr(n, ctx); } 197 | } 198 | 199 | void Parser::ExpectWeak(int n, int follow) { 200 | if (la->kind == n) Get(); 201 | else { 202 | SynErr(n); 203 | while (!StartOf(follow)) Get(); 204 | } 205 | } 206 | 207 | bool Parser::WeakSeparator(int n, int syFol, int repFol) { 208 | if (la->kind == n) {Get(); return true;} 209 | else if (StartOf(repFol)) {return false;} 210 | else { 211 | SynErr(n); 212 | while (!(StartOf(syFol) || StartOf(repFol) || StartOf(0))) { 213 | Get(); 214 | } 215 | return StartOf(syFol); 216 | } 217 | } 218 | 219 | -->productions 220 | 221 | 222 | // If the user declared a method Init and a mehtod Destroy they should 223 | // be called in the contructur and the destructor respctively. 224 | // 225 | // The following templates are used to recognize if the user declared 226 | // the methods Init and Destroy. 227 | 228 | template 229 | struct ParserInitExistsRecognizer { 230 | template 231 | struct ExistsIfInitIsDefinedMarker{}; 232 | 233 | struct InitIsMissingType { 234 | char dummy1; 235 | }; 236 | 237 | struct InitExistsType { 238 | char dummy1; char dummy2; 239 | }; 240 | 241 | // exists always 242 | template 243 | static InitIsMissingType is_here(...); 244 | 245 | // exist only if ExistsIfInitIsDefinedMarker is defined 246 | template 247 | static InitExistsType is_here(ExistsIfInitIsDefinedMarker*); 248 | 249 | enum { InitExists = (sizeof(is_here(NULL)) == sizeof(InitExistsType)) }; 250 | }; 251 | 252 | template 253 | struct ParserDestroyExistsRecognizer { 254 | template 255 | struct ExistsIfDestroyIsDefinedMarker{}; 256 | 257 | struct DestroyIsMissingType { 258 | char dummy1; 259 | }; 260 | 261 | struct DestroyExistsType { 262 | char dummy1; char dummy2; 263 | }; 264 | 265 | // exists always 266 | template 267 | static DestroyIsMissingType is_here(...); 268 | 269 | // exist only if ExistsIfDestroyIsDefinedMarker is defined 270 | template 271 | static DestroyExistsType is_here(ExistsIfDestroyIsDefinedMarker*); 272 | 273 | enum { DestroyExists = (sizeof(is_here(NULL)) == sizeof(DestroyExistsType)) }; 274 | }; 275 | 276 | // The folloing templates are used to call the Init and Destroy methods if they exist. 277 | 278 | // Generic case of the ParserInitCaller, gets used if the Init method is missing 279 | template::InitExists> 280 | struct ParserInitCaller { 281 | static void CallInit(T *t) { 282 | // nothing to do 283 | } 284 | }; 285 | 286 | // True case of the ParserInitCaller, gets used if the Init method exists 287 | template 288 | struct ParserInitCaller { 289 | static void CallInit(T *t) { 290 | t->Init(); 291 | } 292 | }; 293 | 294 | // Generic case of the ParserDestroyCaller, gets used if the Destroy method is missing 295 | template::DestroyExists> 296 | struct ParserDestroyCaller { 297 | static void CallDestroy(T *t) { 298 | // nothing to do 299 | } 300 | }; 301 | 302 | // True case of the ParserDestroyCaller, gets used if the Destroy method exists 303 | template 304 | struct ParserDestroyCaller { 305 | static void CallDestroy(T *t) { 306 | t->Destroy(); 307 | } 308 | }; 309 | 310 | void Parser::Parse() { 311 | d_cur = Token(); 312 | d_next = Token(); 313 | Get(); 314 | -->parseRoot 315 | } 316 | 317 | Parser::Parser(PpLexer *scanner) { 318 | -->constants 319 | ParserInitCaller::CallInit(this); 320 | la = &d_dummy; 321 | minErrDist = 2; 322 | errDist = minErrDist; 323 | this->scanner = scanner; 324 | } 325 | 326 | bool Parser::StartOf(int s) { 327 | const bool T = true; 328 | const bool x = false; 329 | 330 | -->initialization 331 | 332 | return set[s][la->kind]; 333 | } 334 | 335 | Parser::~Parser() { 336 | ParserDestroyCaller::CallDestroy(this); 337 | } 338 | 339 | void Parser::SynErr(int line, int col, int n, const char* ctx, const QString& str, const QString& path ) { 340 | QString s; 341 | QString ctxStr; 342 | if( ctx ) 343 | ctxStr = QString( " in %1" ).arg(ctx); 344 | if( n == 0 ) 345 | s = QString("EOF expected%1").arg(ctxStr); 346 | else if( n < TT_Specials ) 347 | s = QString("'%2' expected%1").arg(ctxStr).arg(tokenTypeString(n)); 348 | else if( n <= TT_Max ) 349 | s = QString("%2 expected%1").arg(ctxStr).arg(tokenTypeString(n)); 350 | else 351 | switch (n) { 352 | -->errors 353 | default: 354 | { 355 | s = QString( "generic error %1").arg(n); 356 | } 357 | break; 358 | } 359 | if( !str.isEmpty() ) 360 | s = QString("%1 %2").arg(s).arg(str); 361 | error(line, col, s, path); 362 | //count++; 363 | } 364 | 365 | -->namespace_close 366 | -------------------------------------------------------------------------------- /LisaHighlighter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Rochus Keller 3 | * 4 | * This file is part of the Lisa Pascal Navigator application. 5 | * 6 | * The following is the license that applies to this copy of the 7 | * application. For a license to use the library under conditions 8 | * other than those described here, please email to me@rochus-keller.ch. 9 | * 10 | * GNU General Public License Usage 11 | * This file may be used under the terms of the GNU General Public 12 | * License (GPL) versions 2.0 or 3.0 as published by the Free Software 13 | * Foundation and appearing in the file LICENSE.GPL included in 14 | * the packaging of this file. Please review the following information 15 | * to ensure GNU General Public Licensing requirements will be met: 16 | * http://www.fsf.org/licensing/licenses/info/GPLv2.html and 17 | * http://www.gnu.org/copyleft/gpl.html. 18 | */ 19 | 20 | // * Adopted from https://github.com/rochus-keller/Oberon/ 21 | 22 | #include "LisaHighlighter.h" 23 | #include "LisaLexer.h" 24 | #include "AsmLexer.h" 25 | #include 26 | using namespace Lisa; 27 | 28 | PascalPainter::PascalPainter(QObject* parent) : 29 | QSyntaxHighlighter(parent) 30 | { 31 | for( int i = 0; i < C_Max; i++ ) 32 | { 33 | d_format[i].setFontWeight(QFont::Normal); 34 | d_format[i].setForeground(Qt::black); 35 | d_format[i].setBackground(Qt::transparent); 36 | } 37 | d_format[C_Num].setForeground(QColor(0, 153, 153)); 38 | d_format[C_Str].setForeground(QColor(208, 16, 64)); 39 | d_format[C_Cmt].setForeground(QColor(153, 153, 136)); 40 | d_format[C_Kw].setForeground(QColor(68, 85, 136)); 41 | d_format[C_Kw].setFontWeight(QFont::Bold); 42 | d_format[C_Op].setForeground(QColor(153, 0, 0)); 43 | d_format[C_Op].setFontWeight(QFont::Bold); 44 | d_format[C_Type].setForeground(QColor(153, 0, 115)); 45 | d_format[C_Type].setFontWeight(QFont::Bold); 46 | d_format[C_Pp].setFontWeight(QFont::Bold); 47 | d_format[C_Pp].setForeground(QColor(0, 128, 0)); 48 | d_format[C_Pp].setBackground(QColor(230, 255, 230)); 49 | d_format[C_Label].setForeground(QColor(251, 138, 0)); 50 | d_format[C_Label].setBackground(QColor(253, 217, 165)); 51 | } 52 | 53 | void PascalPainter::addBuiltIn(const QByteArray& bi) 54 | { 55 | d_builtins << bi; 56 | } 57 | 58 | void PascalPainter::addKeyword(const QByteArray& kw) 59 | { 60 | d_keywords << kw; 61 | } 62 | 63 | QTextCharFormat PascalPainter::formatForCategory(int c) const 64 | { 65 | return d_format[c]; 66 | } 67 | 68 | void PascalPainter::highlightBlock(const QString& text) 69 | { 70 | const int previousBlockState_ = previousBlockState(); 71 | int lexerState = 0, initialBraceDepth = 0; 72 | if (previousBlockState_ != -1) { 73 | lexerState = previousBlockState_ & 0xff; 74 | initialBraceDepth = previousBlockState_ >> 8; 75 | } 76 | 77 | int braceDepth = initialBraceDepth; 78 | 79 | // Protocol: 80 | // lexerState == 1: multi-line (* *) comment 81 | // lexerState == 2: multi-line { } comment 82 | // lexerState == 3: (*$ *) directive 83 | // lexerState == 4: {$ } directive 84 | 85 | int start = 0; 86 | if( lexerState == 1 || lexerState == 3 ) 87 | { 88 | QTextCharFormat f = formatForCategory( lexerState == 1 ? C_Cmt : C_Pp); 89 | int pos = text.indexOf("*)"); 90 | if( pos == -1 ) 91 | { 92 | // the whole block ist part of comment 93 | setFormat( start, text.size(), f ); 94 | setCurrentBlockState( (braceDepth << 8) | lexerState); 95 | return; 96 | }else 97 | { 98 | // End of comment found 99 | pos += 2; 100 | setFormat( start, pos , f ); 101 | lexerState = 0; 102 | braceDepth--; 103 | start = pos; 104 | } 105 | }else if( lexerState == 2 || lexerState == 4 ) 106 | { 107 | QTextCharFormat f = formatForCategory( lexerState == 2 ? C_Cmt : C_Pp); 108 | int pos = text.indexOf('}'); 109 | if( pos == -1 ) 110 | { 111 | // the whole block ist part of comment 112 | setFormat( start, text.size(), f ); 113 | setCurrentBlockState( (braceDepth << 8) | lexerState); 114 | return; 115 | }else 116 | { 117 | // End of comment found 118 | pos += 1; 119 | setFormat( start, pos , f ); 120 | lexerState = 0; 121 | braceDepth--; 122 | start = pos; 123 | } 124 | } 125 | 126 | 127 | Lisa::Lexer lex; 128 | lex.setIgnoreComments(false); 129 | lex.setPackComments(false); 130 | 131 | QList tokens = lex.tokens(text.mid(start)); 132 | for( int i = 0; i < tokens.size(); ++i ) 133 | { 134 | Token &t = tokens[i]; 135 | t.d_colNr += start; 136 | 137 | QTextCharFormat f; 138 | if( t.d_type == Tok_Latt ) 139 | { 140 | if( t.d_val.startsWith("(*$") ) 141 | { 142 | f = formatForCategory(C_Pp); 143 | lexerState = 3; 144 | }else 145 | { 146 | f = formatForCategory(C_Cmt); 147 | lexerState = 1; 148 | } 149 | if( t.d_val.endsWith("*)") ) 150 | lexerState = 0; 151 | else 152 | braceDepth++; 153 | }else if( t.d_type == Tok_Lbrace ) 154 | { 155 | if( t.d_val.startsWith("{$") ) 156 | { 157 | f = formatForCategory(C_Pp); 158 | lexerState = 4; 159 | }else 160 | { 161 | f = formatForCategory(C_Cmt); 162 | lexerState = 2; 163 | } 164 | if( t.d_val.endsWith("}") ) 165 | lexerState = 0; 166 | else 167 | braceDepth++; 168 | }else if( t.d_type == Tok_string_literal ) 169 | f = formatForCategory(C_Str); 170 | else if( t.d_type == Tok_unsigned_real || t.d_type == Tok_digit_sequence || t.d_type == Tok_hex_digit_sequence ) 171 | f = formatForCategory(C_Num); 172 | else if( tokenTypeIsLiteral(t.d_type) ) 173 | { 174 | f = formatForCategory(C_Op); 175 | }else if( tokenTypeIsKeyword(t.d_type) ) 176 | { 177 | f = formatForCategory(C_Kw); 178 | }else if( t.d_type == Tok_identifier ) 179 | { 180 | const QByteArray& name = t.d_val.toUpper(); 181 | /*if( i+1 < tokens.size() && tokens[i+1].d_type == Tok_Colon) 182 | f = formatForCategory(C_Label); 183 | else */if( d_builtins.contains(name) ) 184 | f = formatForCategory(C_Type); 185 | else if( d_keywords.contains(name) ) 186 | f = formatForCategory(C_Kw); 187 | else 188 | f = formatForCategory(C_Ident); 189 | } 190 | 191 | /*if( lexerState == 3 ) 192 | setFormat( startPp, t.d_colNr - startPp + t.d_val.size(), formatForCategory(C_Pp) ); 193 | else */ 194 | if( f.isValid() ) 195 | setFormat( t.d_colNr-1, t.d_val.isEmpty() ? t.d_len : t.d_val.size(), f ); 196 | } 197 | 198 | setCurrentBlockState((braceDepth << 8) | lexerState ); 199 | } 200 | 201 | 202 | 203 | LogPainter::LogPainter(QTextDocument* parent):QSyntaxHighlighter(parent) 204 | { 205 | 206 | } 207 | 208 | void LogPainter::highlightBlock(const QString& text) 209 | { 210 | QColor c = Qt::black; 211 | if( text.startsWith("WRN:") ) 212 | c = Qt::blue; 213 | else if( text.startsWith("ERR:") ) 214 | c = Qt::red; 215 | 216 | setFormat( 0, text.size(), c ); 217 | } 218 | 219 | 220 | AsmPainter::AsmPainter(QObject* parent): 221 | QSyntaxHighlighter(parent) 222 | { 223 | for( int i = 0; i < C_Max; i++ ) 224 | { 225 | d_format[i].setFontWeight(QFont::Normal); 226 | d_format[i].setForeground(Qt::black); 227 | d_format[i].setBackground(Qt::transparent); 228 | } 229 | d_format[C_Num].setForeground(QColor(0, 153, 153)); 230 | d_format[C_Str].setForeground(QColor(208, 16, 64)); 231 | d_format[C_Cmt].setForeground(QColor(153, 153, 136)); 232 | d_format[C_Kw].setForeground(QColor(68, 85, 136)); 233 | d_format[C_Kw].setFontWeight(QFont::Bold); 234 | d_format[C_Op].setForeground(QColor(153, 0, 0)); 235 | d_format[C_Op].setFontWeight(QFont::Bold); 236 | d_format[C_Pp].setFontWeight(QFont::Bold); 237 | d_format[C_Pp].setForeground(QColor(0, 128, 0)); 238 | d_format[C_Pp].setBackground(QColor(230, 255, 230)); 239 | d_format[C_Label].setForeground(QColor(251, 138, 0)); 240 | d_format[C_Label].setBackground(QColor(253, 237, 185)); 241 | } 242 | 243 | void AsmPainter::highlightBlock(const QString& text) 244 | { 245 | const int previousBlockState_ = previousBlockState(); 246 | int lexerState = 0, initialBraceDepth = 0; 247 | if (previousBlockState_ != -1) { 248 | lexerState = previousBlockState_ & 0xff; 249 | initialBraceDepth = previousBlockState_ >> 8; 250 | } 251 | 252 | int braceDepth = initialBraceDepth; 253 | 254 | Asm::Lexer lex; 255 | lex.setIgnoreComments(false); 256 | 257 | QList tokens = lex.tokens(text); 258 | for( int i = 0; i < tokens.size(); ++i ) 259 | { 260 | Asm::Token &t = tokens[i]; 261 | int len = t.d_val.size(); 262 | 263 | QTextCharFormat f; 264 | if( t.d_type == Asm::Tok_Comment ) 265 | f = d_format[C_Cmt]; 266 | else if( t.d_type == Asm::Tok_string ) 267 | f = d_format[C_Str]; 268 | else if( t.d_type == Asm::Tok_number) 269 | f = d_format[C_Num]; 270 | else if( t.d_type == Asm::Tok_label) 271 | f = d_format[C_Label]; 272 | else if( t.d_type == Asm::Tok_dotB || t.d_type == Asm::Tok_dotS || t.d_type == Asm::Tok_dotW 273 | || t.d_type == Asm::Tok_dotL) 274 | f = d_format[C_Kw]; 275 | else if( t.d_type == Asm::Tok_substitute) 276 | f = d_format[C_Pp]; 277 | else if( Asm::tokenTypeIsLiteral(t.d_type) ) 278 | f = d_format[C_Op]; 279 | else if( Asm::tokenTypeIsKeyword(t.d_type) ) 280 | { 281 | if( Asm::Token::isDirective(t.d_type)) 282 | { 283 | f = d_format[C_Pp]; 284 | len++; 285 | }else 286 | f = d_format[C_Kw]; 287 | }else if( t.d_type == Tok_identifier ) 288 | { 289 | if( i+1 < tokens.size() && tokens[i+1].d_type == Tok_Colon ) 290 | f = d_format[C_Label]; 291 | else 292 | f = d_format[C_Ident]; 293 | } 294 | 295 | if( f.isValid() ) 296 | setFormat( t.d_colNr-1, len, f ); 297 | } 298 | 299 | setCurrentBlockState((braceDepth << 8) | lexerState ); 300 | } 301 | -------------------------------------------------------------------------------- /LisaLexer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 3 | ** 4 | ** This file is part of the LisaPascal project. 5 | ** 6 | ** $QT_BEGIN_LICENSE:LGPL21$ 7 | ** GNU Lesser General Public License Usage 8 | ** This file may be used under the terms of the GNU Lesser 9 | ** General Public License version 2.1 or version 3 as published by the Free 10 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 11 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 12 | ** following information to ensure the GNU Lesser General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 14 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 15 | */ 16 | 17 | #include "LisaLexer.h" 18 | #include 19 | #include 20 | using namespace Lisa; 21 | 22 | Lexer::Lexer(): 23 | d_lastToken(Tok_Invalid),d_lineNr(0),d_colNr(0),d_in(0), 24 | d_ignoreComments(true), d_packComments(true),d_sloc(0),d_lineCounted(false) 25 | { 26 | 27 | } 28 | 29 | Lexer::~Lexer() 30 | { 31 | } 32 | 33 | void Lexer::setStream(QIODevice* in, const QString& filePath) 34 | { 35 | d_in = in; 36 | d_lineNr = 0; 37 | d_colNr = 0; 38 | d_lastToken = Tok_Invalid; 39 | d_filePath = filePath; 40 | d_sloc = 0; 41 | d_lineCounted = false; 42 | } 43 | 44 | Token Lexer::nextToken() 45 | { 46 | Token t; 47 | if( !d_buffer.isEmpty() ) 48 | { 49 | t = d_buffer.first(); 50 | d_buffer.pop_front(); 51 | }else 52 | t = nextTokenImp(); 53 | while( t.d_type == Tok_Comment && d_ignoreComments ) 54 | t = nextToken(); 55 | return t; 56 | } 57 | 58 | Token Lexer::peekToken(quint8 lookAhead) 59 | { 60 | Q_ASSERT( lookAhead > 0 ); 61 | while( d_buffer.size() < lookAhead ) 62 | { 63 | Token t = nextTokenImp(); 64 | while( t.d_type == Tok_Comment && d_ignoreComments ) 65 | t = nextTokenImp(); 66 | d_buffer.push_back( t ); 67 | } 68 | return d_buffer[ lookAhead - 1 ]; 69 | } 70 | 71 | QList Lexer::tokens(const QString& code) 72 | { 73 | QBuffer in; 74 | in.setData( code.toLatin1() ); 75 | in.open(QIODevice::ReadOnly); 76 | setStream( &in ); 77 | 78 | QList res; 79 | Token t = nextToken(); 80 | while( t.isValid() ) 81 | { 82 | res << t; 83 | t = nextToken(); 84 | } 85 | return res; 86 | } 87 | 88 | Token Lexer::nextTokenImp() 89 | { 90 | if( d_in == 0 ) 91 | return token(Tok_Eof); 92 | skipWhiteSpace(); 93 | 94 | while( d_colNr >= d_line.size() ) 95 | { 96 | if( d_in->atEnd() ) 97 | { 98 | Token t = token( Tok_Eof, 0 ); 99 | return t; 100 | } 101 | nextLine(); 102 | skipWhiteSpace(); 103 | } 104 | Q_ASSERT( d_colNr < d_line.size() ); 105 | while( d_colNr < d_line.size() ) 106 | { 107 | const char ch = quint8(d_line[d_colNr]); 108 | 109 | if( ch == '\'' ) 110 | return string(); 111 | else if( ch == '$') 112 | return hexnumber(); 113 | else if( ::isalpha(ch) || 114 | ch == '%' || ch == '_' ) 115 | // leading % apparently supported, e.g. in libpl-BLOCKIO2.TEXT.unix.txt 116 | // leading _ apparently supported, e.g. in libdb-HEAP.TEXT.unix.txt 117 | return ident(); 118 | else if( ::isdigit(ch) ) 119 | return number(); 120 | // else 121 | int pos = d_colNr; 122 | TokenType tt = tokenTypeFromString(d_line,&pos); 123 | 124 | if( tt == Tok_Latt ) 125 | return comment(); 126 | else if( tt == Tok_Lbrace ) 127 | return comment(true); 128 | else if( tt == Tok_Invalid || pos == d_colNr ) 129 | return token( Tok_Invalid, 1, QString("unexpected character '%1' %2").arg(char(ch)).arg(int(ch)).toUtf8() ); 130 | else { 131 | const int len = pos - d_colNr; 132 | return token( tt, len, d_line.mid(d_colNr,len) ); 133 | } 134 | } 135 | Q_ASSERT(false); 136 | return token(Tok_Invalid); 137 | } 138 | 139 | int Lexer::skipWhiteSpace() 140 | { 141 | const int colNr = d_colNr; 142 | while( d_colNr < d_line.size() && ( ::isspace( d_line[d_colNr] ) || d_line[d_colNr] == char(0xff) ) ) 143 | d_colNr++; 144 | return d_colNr - colNr; 145 | } 146 | 147 | void Lexer::nextLine() 148 | { 149 | d_colNr = 0; 150 | d_lineNr++; 151 | d_line = d_in->readLine(); 152 | d_lineCounted = false; 153 | 154 | if( d_line.endsWith("\r\n") ) 155 | d_line.chop(2); 156 | else if( d_line.endsWith('\n') || d_line.endsWith('\r') || d_line.endsWith('\025') ) 157 | d_line.chop(1); 158 | } 159 | 160 | int Lexer::lookAhead(int off) const 161 | { 162 | if( int( d_colNr + off ) < d_line.size() ) 163 | { 164 | return d_line[ d_colNr + off ]; 165 | }else 166 | return 0; 167 | } 168 | 169 | Token Lexer::token(TokenType tt, int len, const QByteArray& val) 170 | { 171 | if( tt != Tok_Invalid && tt != Tok_Comment && tt != Tok_Eof ) 172 | countLine(); 173 | Token t( tt, d_lineNr, d_colNr + 1, val ); 174 | d_lastToken = t; 175 | d_colNr += len; 176 | t.d_len = len; 177 | #if 1 178 | if( tt == Tok_identifier ) 179 | t.d_id = Token::toId(val); 180 | #endif 181 | t.d_sourcePath = d_filePath; 182 | return t; 183 | } 184 | 185 | Token Lexer::ident() 186 | { 187 | int off = 1; 188 | while( true ) 189 | { 190 | const char c = lookAhead(off); 191 | if( !::isalnum(c) && c != '_' && c != '%' ) 192 | // % in ident apparently supported, as seen in libfp-FPMODES.TEXT.unix.txt 193 | break; 194 | else 195 | off++; 196 | } 197 | const QByteArray str = d_line.mid(d_colNr, off ); 198 | Q_ASSERT( !str.isEmpty() ); 199 | int pos = 0; 200 | const QByteArray keyword = str.toLower(); 201 | TokenType t = tokenTypeFromString( keyword, &pos ); 202 | if( t != Tok_Invalid && pos != str.size() ) 203 | t = Tok_Invalid; 204 | if( t != Tok_Invalid ) 205 | return token( t, off ); 206 | else 207 | return token( Tok_identifier, off, str ); 208 | } 209 | 210 | static inline bool isHexDigit( char c ) 211 | { 212 | return ::isdigit(c) || c == 'A' || c == 'B' || c == 'C' || c == 'D' || c == 'E' || c == 'F' 213 | || c == 'a' || c == 'b' || c == 'c' || c == 'd' || c == 'e' || c == 'f'; 214 | } 215 | 216 | Token Lexer::number() 217 | { 218 | // unsigned_real ::= // digit_sequence [ '.' digit_sequence ] [ scale_factor ] 219 | // scale_factor ::= ('E' | 'e') [sign] digit_sequence 220 | // digit_sequence ::= // digit { digit } 221 | 222 | 223 | int off = 1; 224 | while( true ) 225 | { 226 | const char c = lookAhead(off); 227 | if( !::isdigit(c) ) 228 | break; 229 | else 230 | off++; 231 | } 232 | bool isReal = false; 233 | if( lookAhead(off) == '.' && lookAhead(off+1) != '.' ) 234 | { 235 | isReal = true; 236 | off++; 237 | if( !::isdigit(lookAhead(off))) 238 | return token( Tok_Invalid, off, "invalid real, digit expected after dot" ); 239 | while( true ) 240 | { 241 | const char c = lookAhead(off); 242 | if( !::isdigit(c) ) 243 | break; 244 | else 245 | off++; 246 | } 247 | } 248 | if( lookAhead(off) == 'E' || lookAhead(off) == 'e' ) 249 | { 250 | isReal = true; 251 | off++; 252 | char o = lookAhead(off); 253 | if( o == '+' || o == '-' ) 254 | { 255 | off++; 256 | o = lookAhead(off); 257 | } 258 | if( !::isdigit(o) ) 259 | return token( Tok_Invalid, off, "invalid real, digit expected after exponent" ); 260 | while( true ) 261 | { 262 | const char c = lookAhead(off); 263 | if( !::isdigit(c) ) 264 | break; 265 | else 266 | off++; 267 | } 268 | } 269 | const QByteArray str = d_line.mid(d_colNr, off ); 270 | Q_ASSERT( !str.isEmpty() ); 271 | if( isReal) 272 | return token( Tok_unsigned_real, off, str ); 273 | else 274 | return token( Tok_digit_sequence, off, str ); 275 | } 276 | 277 | Token Lexer::hexnumber() 278 | { 279 | // hex_digit_sequence ::= // '$' hex_digit { hex_digit } 280 | // hex_digit ::= digit | 'A'..'F' 281 | 282 | int off = 1; 283 | while( true ) 284 | { 285 | const char c = lookAhead(off); 286 | if( !isHexDigit(c) ) 287 | break; 288 | else 289 | off++; 290 | } 291 | const QByteArray str = d_line.mid(d_colNr, off ); 292 | Q_ASSERT( !str.isEmpty() ); 293 | return token( Tok_digit_sequence, off, str ); 294 | } 295 | 296 | Token Lexer::comment(bool brace) 297 | { 298 | const int startLine = d_lineNr; 299 | const int startCol = d_colNr; 300 | // startLine and startCol point to first char of (* or { 301 | 302 | const QByteArray tag = brace ? "}" : "*)"; 303 | int pos = d_line.indexOf(tag,d_colNr); 304 | 305 | QByteArray str; 306 | bool terminated = false; 307 | if( pos < 0 ) 308 | str = d_line.mid(d_colNr); 309 | else 310 | { 311 | terminated = true; 312 | pos += tag.size(); 313 | str = d_line.mid(d_colNr,pos-d_colNr); 314 | } 315 | while( !terminated && !d_in->atEnd() ) 316 | { 317 | nextLine(); 318 | pos = d_line.indexOf(tag,d_colNr); 319 | if( !str.isEmpty() ) 320 | str += '\n'; 321 | if( pos < 0 ) 322 | str += d_line.mid(d_colNr); 323 | else 324 | { 325 | terminated = true; 326 | pos += tag.size(); 327 | str += d_line.mid(d_colNr,pos-d_colNr); 328 | } 329 | } 330 | if( d_packComments && !terminated && d_in->atEnd() ) 331 | { 332 | d_colNr = d_line.size(); 333 | Token t( Tok_Invalid, startLine, startCol + 1, "non-terminated comment" ); 334 | t.d_sourcePath = d_filePath; 335 | return t; 336 | } 337 | // Col + 1 weil wir immer bei Spalte 1 beginnen, nicht bei Spalte 0 338 | Token t( ( d_packComments ? Tok_Comment : ( brace ? Tok_Lbrace : Tok_Latt ) ), startLine, startCol + 1, str ); 339 | d_lastToken = t; 340 | d_colNr = pos; 341 | t.d_sourcePath = d_filePath; 342 | return t; 343 | } 344 | 345 | Token Lexer::string() 346 | { 347 | int off = 1; 348 | while( true ) 349 | { 350 | const char c = lookAhead(off); 351 | off++; 352 | if( c == '\'') 353 | { 354 | if( lookAhead(off) == '\'') 355 | off++; 356 | else 357 | break; 358 | } 359 | if( c == 0 ) 360 | return token( Tok_Invalid, off, "non-terminated string" ); 361 | } 362 | const QByteArray str = d_line.mid(d_colNr, off ); 363 | return token( Tok_string_literal, off, str ); 364 | } 365 | 366 | void Lexer::countLine() 367 | { 368 | if( !d_lineCounted ) 369 | d_sloc++; 370 | d_lineCounted = true; 371 | } 372 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ** Copyright (C) 2023 Rochus Keller (me@rochus-keller.ch) 3 | ** 4 | ** This file is part of the LisaPascal project. 5 | ** 6 | ** $QT_BEGIN_LICENSE:LGPL21$ 7 | ** GNU Lesser General Public License Usage 8 | ** This file may be used under the terms of the GNU Lesser 9 | ** General Public License version 2.1 or version 3 as published by the Free 10 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and 11 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the 12 | ** following information to ensure the GNU Lesser General Public License 13 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 14 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "LisaPpLexer.h" 24 | #include "LisaParser.h" 25 | #include "Converter.h" 26 | #include "LisaFileSystem.h" 27 | using namespace Lisa; 28 | 29 | static void dump(QTextStream& out, const SynTree* node, int level) 30 | { 31 | QByteArray str; 32 | if( node->d_tok.d_type == Tok_Invalid ) 33 | level--; 34 | else if( node->d_tok.d_type < SynTree::R_First ) 35 | { 36 | if( tokenTypeIsKeyword( node->d_tok.d_type ) ) 37 | str = tokenTypeString(node->d_tok.d_type); 38 | else if( node->d_tok.d_type > TT_Specials ) 39 | str = QByteArray("\"") + node->d_tok.d_val + QByteArray("\""); 40 | else 41 | str = QByteArray("\"") + tokenTypeString(node->d_tok.d_type) + QByteArray("\""); 42 | 43 | }else 44 | str = SynTree::rToStr( node->d_tok.d_type ); 45 | if( !str.isEmpty() ) 46 | { 47 | str += QByteArray("\t") + QFileInfo(node->d_tok.d_sourcePath).baseName().toUtf8() + 48 | ":" + QByteArray::number(node->d_tok.d_lineNr) + 49 | ":" + QByteArray::number(node->d_tok.d_colNr); 50 | QByteArray ws; 51 | for( int i = 0; i < level; i++ ) 52 | ws += "| "; 53 | str = ws + str; 54 | out << str.data() << endl; 55 | } 56 | foreach( SynTree* sub, node->d_children ) 57 | dump( out, sub, level + 1 ); 58 | } 59 | 60 | static void compareFiles( const QStringList& files, int off ) 61 | { 62 | foreach( const QString& f, files ) 63 | { 64 | QCryptographicHash hash(QCryptographicHash::Md5); 65 | QFile in(f); 66 | in.open(QIODevice::ReadOnly); 67 | hash.addData(in.readAll()); 68 | QFileInfo info(f); 69 | qDebug() << f.mid(off) << "\t" << info.fileName().toLower() << "\t" << hash.result().toHex(); 70 | } 71 | } 72 | 73 | static void checkDir( const QStringList& files, int off) 74 | { 75 | foreach( const QString& f, files ) 76 | { 77 | QFile in(f); 78 | if( !in.open(QIODevice::ReadOnly) ) 79 | { 80 | qCritical() << "cannot open file for reading:" << f; 81 | return; 82 | } 83 | if( Converter::detectPascal(&in) == Converter::Unknown ) 84 | continue; 85 | QFileInfo info(f); 86 | QDir dir = info.absoluteDir(); 87 | const QString fileName = info.fileName().toLower(); 88 | QString name; 89 | if( fileName.endsWith("text.unix.txt") ) 90 | { 91 | name = fileName; 92 | name.chop(13); 93 | }else 94 | name = info.baseName(); 95 | const QStringList parts = name.contains('-') ? name.split('-') : name.split('.'); 96 | // name.split(QRegExp("[\-,\.]")); 97 | if( parts.size() == 1 ) 98 | qDebug() << "** file with no dir:" << f.mid(off); 99 | else if( parts.size() >= 2 ) 100 | { 101 | const QString dirname = dir.dirName().toLower(); 102 | if( parts.first() != dirname ) 103 | qDebug() << "** actual and virtual dir differ:" << f.mid(off) << dirname << parts.first(); 104 | else if( parts.size() > 2 ) 105 | qDebug() << "** more than two parts:" << f.mid(off) << dirname << parts.first(); 106 | } 107 | } 108 | } 109 | 110 | #define _USE_EBNF_STUDIO_PARSER_ 111 | 112 | class Lex 113 | #ifdef _USE_EBNF_STUDIO_PARSER_ 114 | : public Scanner 115 | #endif 116 | { 117 | public: 118 | PpLexer lex; 119 | Token next() 120 | { 121 | return lex.nextToken(); 122 | } 123 | 124 | Token peek(int offset) 125 | { 126 | return lex.peekToken(offset); 127 | } 128 | 129 | Lex(FileSystem*fs):lex(fs){} 130 | }; 131 | 132 | static void runParser(const QString& root) 133 | { 134 | FileSystem fs; 135 | fs.load(root); 136 | // fs.getRoot().dump(); 137 | 138 | QList files = fs.getAllPas(); 139 | int ok = 0; 140 | QElapsedTimer timer; 141 | timer.start(); 142 | foreach( const FileSystem::File* file, files ) 143 | { 144 | const QString path = file->getVirtualPath(); 145 | Lex lex(&fs); 146 | lex.lex.reset(file->d_realPath); 147 | #ifdef _USE_EBNF_STUDIO_PARSER_ 148 | Parser p(&lex); 149 | #else 150 | Parser p(&lex.lex); 151 | #endif 152 | qDebug() << "**** parsing" << path; 153 | p.RunParser(); 154 | if( !p.errors.isEmpty() ) 155 | { 156 | foreach( const Parser::Error& e, p.errors ) 157 | qCritical() << e.path.mid(root.size()) << e.row << e.col << e.msg; 158 | // qCritical() << fs.findFile(e.path)->getVirtualPath() << e.row << e.col << e.msg; 159 | 160 | }else 161 | { 162 | ok++; 163 | qDebug() << "ok"; 164 | } 165 | #if 0 166 | QFile out(file->d_realPath + ".st"); 167 | out.open(QIODevice::WriteOnly); 168 | QTextStream s(&out); 169 | #ifdef _USE_EBNF_STUDIO_PARSER_ 170 | dump(s,&p.root,0); 171 | #else 172 | dump(s,&p.d_root,0); 173 | #endif 174 | #endif 175 | } 176 | qDebug() << "#### finished with" << ok << "files ok of total" << files.size() << "files" 177 | << "in" << timer.elapsed() << " [ms]"; 178 | } 179 | 180 | static void runParser(const QString& root, const QString& path) 181 | { 182 | FileSystem fs; 183 | fs.load(root); 184 | 185 | Lex lex(&fs); 186 | lex.lex.reset(path); 187 | #ifdef _USE_EBNF_STUDIO_PARSER_ 188 | Parser p(&lex); 189 | #else 190 | Parser p(&lex.lex); 191 | #endif 192 | qDebug() << "**** parsing" << path; 193 | p.RunParser(); 194 | if( !p.errors.isEmpty() ) 195 | { 196 | foreach( const Parser::Error& e, p.errors ) 197 | qCritical() << e.path.mid(root.size()) << e.row << e.col << e.msg; 198 | // qCritical() << fs.findFile(e.path)->getVirtualPath() << e.row << e.col << e.msg; 199 | 200 | }else 201 | { 202 | qDebug() << "ok"; 203 | } 204 | #if 0 205 | QFile out(path + ".st"); 206 | out.open(QIODevice::WriteOnly); 207 | QTextStream s(&out); 208 | #ifdef _USE_EBNF_STUDIO_PARSER_ 209 | dump(s,&p.root,0); 210 | #else 211 | dump(s,&p.d_root,0); 212 | #endif 213 | #endif 214 | } 215 | 216 | static void checkTokens(const QStringList& files) 217 | { 218 | foreach( const QString& file, files ) 219 | { 220 | QFile in(file); 221 | if( !in.open(QIODevice::ReadOnly) ) 222 | { 223 | qCritical() << "cannot open file for reading:" << file; 224 | return; 225 | } 226 | Lexer lex; 227 | lex.setStream(&in); 228 | lex.setIgnoreComments(false); 229 | Token t = lex.nextToken(); 230 | while(t.isValid()) 231 | { 232 | // qDebug() << tokenTypeName(t.d_type) << t.d_lineNr << t.d_colNr << t.d_val; 233 | if( t.d_type == Tok_Comment && ( t.d_val.startsWith("{$") || t.d_val.startsWith("(*$") ) ) 234 | { 235 | //const QByteArray tmp = t.d_val.toUpper(); 236 | //if( tmp.startsWith("{$DECL") || tmp.startsWith("(*$DECL") ) 237 | qDebug() << "directive" << QFileInfo(file).fileName() << t.d_lineNr << t.d_colNr << t.d_val; 238 | } 239 | t = lex.nextToken(); 240 | } 241 | } 242 | } 243 | 244 | #if 0 245 | static void checkIncludes(const QStringList& files, int off) 246 | { 247 | QMap count; 248 | foreach( const QString& file, files ) 249 | { 250 | QFile in(file); 251 | if( !in.open(QIODevice::ReadOnly) ) 252 | { 253 | qCritical() << "cannot open file for reading:" << file; 254 | return; 255 | } 256 | Lexer lex; 257 | lex.setStream(&in); 258 | Parser p(&lex); 259 | lex.setStream(&in); 260 | lex.setIgnoreComments(false); 261 | Token t = lex.nextToken(); 262 | while(t.isValid()) 263 | { 264 | if( t.d_type == Tok_Comment && t.d_val.startsWith("{$") ) 265 | { 266 | const int pos = t.d_val.indexOf(' '); 267 | if( pos >= 0 ) 268 | { 269 | const QByteArray d = t.d_val.left(pos).mid(2).toUpper(); 270 | count[d]++; 271 | if( d == "I" ) 272 | qDebug() << "***" << in.fileName().mid(off) << "includes" << 273 | t.d_val.left(t.d_val.size()-1).mid(pos).trimmed(); 274 | } 275 | } 276 | t = lex.nextToken(); 277 | } 278 | 279 | } 280 | qDebug() << count; 281 | } 282 | #endif 283 | 284 | static void convert(const QString& root) 285 | { 286 | QFileInfo info(root); 287 | if( info.isDir() ) 288 | { 289 | QDir from = info.dir(); 290 | QDir to( from.path() + "/converted" ); 291 | Converter::convert(from,to); 292 | } 293 | } 294 | 295 | static void checkFileNames(const QStringList& files) 296 | { 297 | foreach( const QString& file, files ) 298 | { 299 | QFile in(file); 300 | if( !in.open(QIODevice::ReadOnly) ) 301 | { 302 | qCritical() << "cannot open file for reading:" << file; 303 | return; 304 | } 305 | if( Converter::detectPascal(&in) != Converter::Unknown ) 306 | { 307 | const QString name = QFileInfo(file).fileName().toLower(); 308 | if( !name.endsWith("text.unix.txt") ) 309 | qDebug() << "** Pascal file with unexpected suffix:" << files; 310 | } 311 | } 312 | } 313 | 314 | int main(int argc, char *argv[]) 315 | { 316 | QCoreApplication a(argc, argv); 317 | 318 | if( a.arguments().size() <= 1 ) 319 | return -1; 320 | 321 | #if 0 322 | QStringList files; 323 | 324 | int off = 0; 325 | QFileInfo info(a.arguments()[1]); 326 | if( info.isDir() ) 327 | { 328 | files = Converter::collectFiles(info.dir(),QStringList() << "*.txt"); 329 | off = a.arguments()[1].size(); 330 | }else 331 | files << a.arguments()[1]; 332 | 333 | //convert(a.arguments()[1]); 334 | //compareFiles(files,off); 335 | //checkIncludes(files,off); 336 | //checkDir(files,off); 337 | //checkFileNames(files); 338 | //checkTokens(files); 339 | #else 340 | QFileInfo info(a.arguments()[1]); 341 | if( info.isDir() ) 342 | runParser(a.arguments()[1]); 343 | else 344 | runParser(info.absolutePath(),info.absoluteFilePath()); 345 | #endif 346 | 347 | return 0; 348 | } 349 | -------------------------------------------------------------------------------- /images/copyright.txt: -------------------------------------------------------------------------------- 1 | This application makes use of some icons of the Lazarus IDE. 2 | The following terms apply: 3 | 4 | LICENSE AND COPYRIGHT INFORMATION ABOUT IMAGES USED IN LAZARUS IDE 5 | ------------------------------------------------------------------ 6 | 7 | Public domain Tango https://packages.debian.org/sid/tango-icon-theme 8 | - ToDo 9 | 10 | Creative Commons. Author and source mentioned. 11 | - pkg_sortalphabetically.png, originally sort_ascend.png by Momenticons (got from findicons.com) 12 | 13 | --- 14 | 15 | Following images were created by FTurtle (this holds also for the equally named 16 | files with appendix _150 and _200): 17 | 18 | actions directory: 19 | arrow__darkgreen_down.png 20 | arrow__darkgreen_left.png 21 | arrow__darkgreen_right.png 22 | arrow__darkgreen_up.png 23 | arrow__darkred_down.png 24 | arrow__darkred_left.png 25 | arrow__darkred_right.png 26 | arrow__darkred_up.png 27 | autocomplete.png 28 | filter_any_place.png (used 'item_filter.png' created by Roland Hahn) 29 | filter_from_begin.png (used 'item_filter.png' created by Roland Hahn) 30 | laz_open.png 31 | laz_open_recent.png 32 | laz_open_unit.png 33 | laz_save.png 34 | next_word.png 35 | pastel_colors.png 36 | previous_word.png 37 | restore_default.png 38 | restore_defaults.png 39 | tab_close_All.png 40 | tab_close_L.png 41 | tab_close_LR.png 42 | tab_close_R.png 43 | 44 | debugger directory: 45 | debugger_call_stack.png 46 | 47 | designer directory: 48 | anchor_bottom_bottom.png 49 | anchor_bottom_center.png 50 | anchor_bottom_top.png 51 | anchor_left_center.png 52 | anchor_left_left.png 53 | anchor_left_right.png 54 | anchor_right_center.png 55 | anchor_right_left.png 56 | anchor_right_right.png 57 | anchor_top_bottom.png 58 | anchor_top_center.png 59 | anchor_top_top.png 60 | tab_order.png 61 | 62 | items directory: 63 | datamodule_designer.png (used 'tdatasource.png' created by Roland Hahn) 64 | form_designer.png 65 | frame_designer.png 66 | item_form.png 67 | item_project.png 68 | item_project_source.png 69 | item_unit.png 70 | 71 | menu directory: 72 | menu_add_jump_point_to_history.png 73 | menu_clear_all_bookmarks.png 74 | menu_clear_file_bookmarks.png 75 | menu_close.png 76 | menu_close_all.png 77 | menu_file_revert.png 78 | menu_goto_bookmark0.png 79 | menu_goto_bookmark1.png 80 | menu_goto_bookmark2.png 81 | menu_goto_bookmark3.png 82 | menu_goto_bookmark4.png 83 | menu_goto_bookmark5.png 84 | menu_goto_bookmark6.png 85 | menu_goto_bookmark7.png 86 | menu_goto_bookmark8.png 87 | menu_goto_bookmark9.png 88 | menu_goto_bookmarks.png 89 | menu_goto_line.png 90 | menu_indent.png 91 | menu_jumpto_implementation.png 92 | menu_jumpto_implementationuses.png 93 | menu_jumpto_initialization.png 94 | menu_jumpto_interface.png 95 | menu_jumpto_interfaceuses.png 96 | menu_jumpto_section.png 97 | menu_manage_desktops.png 98 | menu_manage_source_editors.png 99 | menu_new.png 100 | menu_new_form.png 101 | menu_new_search.png 102 | menu_new_unit.png 103 | menu_project_add.png 104 | menu_project_close.png 105 | menu_project_from_file.png 106 | menu_project_inspector.png 107 | menu_project_new.png 108 | menu_project_open.png 109 | menu_project_open_recent.png 110 | menu_project_options.png 111 | menu_project_remove.png 112 | menu_project_save.png 113 | menu_project_save_as.png 114 | menu_redo.png 115 | menu_saveas.png 116 | menu_save_all.png 117 | menu_search_files.png 118 | menu_search_find.png 119 | menu_search_find_next.png 120 | menu_search_find_previous.png 121 | menu_search_incremental.png 122 | menu_search_jumpback.png 123 | menu_search_jumpforward.png 124 | menu_search_next_bookmark.png 125 | menu_search_next_error.png (used 'state_error.png' created by Roland Hahn) 126 | menu_search_openfile_atcursor.png 127 | menu_search_previous_bookmark.png 128 | menu_search_previous_error.png (used 'state_error.png' created by Roland Hahn) 129 | menu_search_procedure_list.png (used 'cc_procedure.png' created by Roland Hahn) 130 | menu_search_replace.png 131 | menu_search_set_bookmark.png 132 | menu_set_free_bookmark.png 133 | menu_stepinto.png 134 | menu_stepout.png 135 | menu_stepover.png 136 | menu_toggle_bookmark0.png 137 | menu_toggle_bookmark1.png 138 | menu_toggle_bookmark2.png 139 | menu_toggle_bookmark3.png 140 | menu_toggle_bookmark4.png 141 | menu_toggle_bookmark5.png 142 | menu_toggle_bookmark6.png 143 | menu_toggle_bookmark7.png 144 | menu_toggle_bookmark8.png 145 | menu_toggle_bookmark9.png 146 | menu_toggle_bookmarks.png 147 | menu_tool_check_lfm.png 148 | menu_tool_syntax_check.png 149 | menu_undo.png 150 | menu_unindent.png 151 | menu_view_anchor_editor.png 152 | menu_view_code_browser.png 153 | menu_view_code_explorer.png 154 | menu_view_components.png 155 | menu_view_forms.png 156 | menu_view_inspector.png 157 | menu_view_jump_history.png 158 | menu_view_messages.png 159 | menu_view_restriction_browser.png 160 | menu_view_search_results.png 161 | menu_view_source_editor.png 162 | menu_view_toggle_form_unit.png 163 | menu_view_units.png 164 | menu_view_unit_info.png 165 | 166 | sourceeditor directory: 167 | bookmark0.png 168 | bookmark1.png 169 | bookmark2.png 170 | bookmark3.png 171 | bookmark4.png 172 | bookmark5.png 173 | bookmark6.png 174 | bookmark7.png 175 | bookmark8.png 176 | bookmark9.png 177 | 178 | states directory: 179 | state_warning.png 180 | state_warning_69.png 181 | state_warning_75.png 182 | state_warning_150.png 183 | state_warning_200.png 184 | 185 | packages directory 186 | pkg_open_recent.png (used 'pkg_open.png' created by Roland Hahn) 187 | 188 | -------------------------------------------------------------------------------- 189 | 190 | The following component palette and menu/toolbar icons (including equally named 191 | files with appendix _150 and _200) were created by Roland Hahn (no restrictions). 192 | svg sources can be found on Lazarus Components and Code Repository, 193 | folder image_sources. 194 | 195 | actions folder: 196 | execute.png 197 | laz_cancel.png 198 | laz_edit.png 199 | laz_export.png 200 | laz_highlighter.png 201 | laz_refresh.png 202 | laz_tick.png 203 | laz_wand.png 204 | preferences.png 205 | 206 | codeexplorer folder: 207 | ce_classinterface.png 208 | ce_cycleimplementation.png 209 | ce_cycleinterface.png 210 | ce_default.png 211 | ce_deprecated.png 212 | ce_experimental.png 213 | ce_finalization.png 214 | ce_helper.png 215 | ce_implementation.png 216 | ce_initialization.png 217 | ce_interface.png 218 | ce_library.png 219 | ce_platform.png 220 | ce_program.png 221 | ce_property_readonly.png 222 | ce_section.png 223 | ce_unimplemented.png 224 | show_category.png 225 | show_source.png 226 | 227 | codetoolsdefines folder: 228 | da_block.png 229 | da_define.png 230 | da_definerecurse.png 231 | da_define.png 232 | da_directory.png 233 | da_else.png 234 | da_elseif.png 235 | da_if.png 236 | da_ifdef.png 237 | da_ifndef.png 238 | da_undefine.png 239 | da_undefineall.png 240 | da_undefinerecurse.png 241 | 242 | components folder: 243 | default.png 244 | tactionlist.png 245 | tapplicationproperties.png 246 | tarrow.png 247 | tasyncprocess.png 248 | tbevel.png 249 | tbitbtn.png 250 | tbufdataset.png 251 | tbutton.png 252 | tbuttonpanel.png 253 | tcalcedit.png 254 | tcalculatordialog.png 255 | tcalendar.png 256 | tcalendardialog.png 257 | tcheckbox.png 258 | tcheckcombobox.png 259 | tcheckgroup.png 260 | tchecklistbox.png 261 | tcolorbox.png 262 | tcolorbutton.png 263 | tcolordialog.png 264 | tcolorlistbox.png 265 | tcombobox.png 266 | tcomboboxex.png 267 | tcontrolbar.png 268 | tcoolbar.png 269 | tdatasource.png 270 | tdateedit.png 271 | tdbcalendar.png 272 | tdbcheckbox.png 273 | tdbcombobox.png 274 | tdbdateedit.png 275 | tdbedit.png 276 | tdbgrid.png 277 | tdbgroupbox.png 278 | tdbimage.png 279 | tdblistbox.png 280 | tdblookupcombobox.png 281 | tdblookuplistbox.png 282 | tdbmemo.png 283 | tdbnavigator.png 284 | tdbradiogroup.png 285 | tdbtext.png 286 | tdirectoryedit.png 287 | tdrawgrid.png 288 | tedit.png 289 | teditbutton.png 290 | teventlog.png 291 | tfilelistbox.png 292 | tfilenameedit.png 293 | tfiltercombobox.png 294 | tfinddialog.png 295 | tfloatspinedit.png 296 | tflowpanel.png 297 | tfontdialog.png 298 | tframe.png 299 | tgroupbox.png 300 | theadercontrol.png 301 | thtmlbrowserhelpviewer.png 302 | thtmlhelpdatabase.png 303 | tidedialoglayoutstorage.png 304 | tidletimer.png 305 | timage.png 306 | timagelist.png 307 | tinipropstorage.png 308 | tjsonpropstorage.png 309 | tlabel.png 310 | tlabelededit.png 311 | tlazcomponentqueue.png 312 | tlistbox.png 313 | tlistview.png 314 | tmainmenu.png 315 | tmaskedit.png 316 | tmemo.png 317 | tmouse.png 318 | tnotebook.png 319 | topendialog.png 320 | topenpicturedialog.png 321 | tpagecontrol.png 322 | tpaintbox.png 323 | tpairsplitter.png 324 | tpanel.png 325 | tpicture.png 326 | tpopupmenu.png 327 | tpopupnotifier.png 328 | tprocess.png 329 | tprocessutf8.png 330 | tprogressbar.png 331 | tradiobutton.png 332 | tradiogroup.png 333 | treplacedialog.png 334 | tsavedialog.png 335 | tsavepicturedialog.png 336 | tscrollbar.png 337 | tscrollbox.png 338 | tselectdirectorydialog.png 339 | tservicemanager.png 340 | tshape.png 341 | tshelllistview.png 342 | tshelltreeview.png 343 | tsimpleipcclient.png 344 | tsimpleipcserver.png 345 | tspeedbutton.png 346 | tspinedit.png 347 | tsplitter.png 348 | tstatictext.png 349 | tstatusbar.png 350 | tstringgrid.png 351 | ttabcontrol.png 352 | ttaskdialog.png 353 | ttimeedit.png 354 | ttimer.png 355 | ttogglebox.png 356 | ttoolbar.png 357 | ttrackbar.png 358 | ttrayicon.png 359 | ttreeview.png 360 | tupdown.png 361 | tvaluelisteditor.png 362 | txmlconfig.png 363 | txmlpropstorage.png 364 | unregisteredcomponent.png 365 | 366 | componenttreeview directory 367 | oi_box.png 368 | oi_collection.png 369 | oi_comp.png 370 | oi_control.png 371 | oi_form.png 372 | oi_item.pnt 373 | 374 | debugger directory 375 | callstack_bottom.png 376 | callstack_goto.png 377 | callstack_more.png 378 | callstack_show.png 379 | callstack_top.png 380 | camera.png 381 | camera_add.png 382 | camera_go.png 383 | clock.png 384 | debugger.png 385 | debugger_breakpoints.png 386 | debugger_disable.png 387 | debugger_disable_all.png 388 | debugger_enable.png 389 | debugger_enable_all.png 390 | debugger_evaluate.png 391 | debugger_event_log.png 392 | debugger_inspect.png 393 | debugger_modify.png 394 | debugger_options.png 395 | debugger_output.png 396 | debugger_power.png 397 | debugger_power_grey.png 398 | debugger_show_execution_point.png 399 | debugger_watches.png 400 | evaluate_no_hist.png 401 | evaluate_up.png 402 | 403 | designer directory 404 | align.png 405 | mirror_horizontal.png 406 | mirror_vertical.png 407 | order_back_one.png 408 | order_forward_one.png 409 | order_move_back.png 410 | order_move_front.png 411 | scale.png 412 | size.png 413 | 414 | items directory 415 | item_character.png 416 | item_filter.png 417 | item_font.png 418 | item_keyboard.png 419 | item_library.png 420 | item_package.png 421 | item_todo.png 422 | 423 | lazdoc directory 424 | formatbold.png 425 | formatitalic.png 426 | formatunderline.png 427 | insertcodetag.png 428 | insertremark.png 429 | insertvartag.png 430 | 431 | menu directory 432 | menu_abort_build.png 433 | menu_build.png 434 | menu_build_all.png 435 | menu_build_clean.png 436 | menu_build_file.png 437 | menu_build_lazarus.png 438 | menu_build_run_file.png 439 | menu_clean.png 440 | menu_codetoolsdefineseditor.png 441 | menu_codetoolsoptions.png 442 | menu_comment.png 443 | menu_compiler_options.png 444 | menu_configure_build_lazarus.png 445 | menu_configure_help.png 446 | menu_editor_options.png 447 | menu_edit_lowercase.png 448 | menu_edit_sort.png 449 | menu_edit_uppercase.png 450 | menu_environment_options.png 451 | menu_exit.png 452 | menu_file_print.png 453 | menu_information.png 454 | menu_jumpto_procedurebegin.png 455 | menu_jumptoprocedureheader.png 456 | menu_pause.png 457 | menu_quick_compile.png 458 | menu_reportingbug.png 459 | menu_reset_debugger.png 460 | menu_run_file.png 461 | menu_run_cursor.png 462 | menu_run_file.png 463 | menu_run_parameters.png 464 | menu_run_withoutdebuggin.png 465 | menu_search_set_bookmark.png 466 | menu_select_all.png 467 | menu_stepinto_instr.png 468 | menu_stepinto_instr_run_file.png 469 | menu_stepover:instr.png 470 | menu_stop.png 471 | menu_tool_del_to_laz.png 472 | menu_tool_del_to_laz_form.png 473 | menu_tool_del_to_laz_pkg.png 474 | menu_tool_del_to_laz_project.png 475 | menu_tool_del_to_laz_unit.png 476 | menu_tool_diff.png 477 | menu_tool_make_resourcestring.png 478 | menu_uncomment.png 479 | menu_view_todo.png 480 | SelCompPage.png 481 | 482 | packages directory 483 | pkg_add.png 484 | pkg_binary.png 485 | pkg_compile.png 486 | pkg_conflict.png 487 | pkg_core_overlay.png 488 | pkg_design_overlay.png 489 | pkg_files.png 490 | pkg_fpc_overlay.png 491 | pkg_graph.png 492 | pkg_hierarchical.png 493 | pkg_include.png 494 | pkg_inherited.png 495 | pkg_install.png 496 | pkg_installed.png 497 | pkg_issues.png 498 | pkg_lazarus_overlay.png 499 | pkg_lfm.png 500 | pkg_lrs.png 501 | pkg_open.png 502 | pkg_package_autoinstall.png 503 | pkg_package_circle.png 504 | pkg_package_uninstall.png 505 | pkg_properties.png 506 | pkg_registerunit.png 507 | pkg_removedfiles.png 508 | pkg_removedrequired.png 509 | pkg_required.png 510 | pkg_runtime_overlay.png 511 | pkg_sortalphabetically.png 512 | pkg_text.png 513 | pkg_unit.png 514 | 515 | states directory (also variants with appendix _69 and _75) 516 | quickfix 517 | state_circular_reference 518 | state_error 519 | state_fatal 520 | state_hint 521 | state_information 522 | state_note 523 | state_not_found 524 | state_unit_circular_reference 525 | state_unknown 526 | state_warning 527 | 528 | windows directory 529 | win_formeditor.ico 530 | win_main.ico 531 | win_message.ico 532 | win_objectinspector.ico 533 | win_sourceditor.ico 534 | 535 | images root folder 536 | folder.png 537 | includefile.ico 538 | LazarusForm.ico 539 | lazaruspackage.ico 540 | LazarusSource.ico 541 | lprfile.ico 542 | template.png 543 | 544 | ----------------------------------------- 545 | 546 | The following images were created by regs: 547 | 548 | codecompletion folder: 549 | cc_class.png, cc_class_150.png, cc_class_200.png 550 | cc_constant.png, cc_constant_150.png, cc_constant_200.png 551 | cc_constructor.png, cc_constructor_150.png, cc_constructor_200.png 552 | cc_destructor.png, cc_destructor_150.png, cc_destructor_200.png 553 | cc_enum.png, cc_enum_150.png, cc_enum_200.png 554 | cc_function.png, cc_function_150.png, cc_function_200.png 555 | cc_label.png, cc_label_150.png, cc_label_200.png 556 | cc_namespace.png, cc_namespace_150.png, cc_namespace_200.png 557 | cc_procedure.png, cc_procedure_150.png, cc_procedure_200.png 558 | cc_property.png, cc_property_150.png, cc_property_200.png 559 | cc_property_ro.png, cc_property_ro_150.png, cc_property_ro_200.png 560 | cc_record.png, cc_record_150.png, cc_record_200.png 561 | cc_type.png, cc_type_150.png, cc_type_200.png 562 | cc_unit.png, cc_unit_150.png, cc_unit_200.png 563 | cc_variable.png, cc_variable_150.png, cc_variable_200.png 564 | -------------------------------------------------------------------------------- /syntax/LisaPascal.ebnf: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Rochus Keller and others 2 | 3 | // started with the Pascal EBNF found at https://www.icosaedro.it/bnf_chk/ebnf-pascal.txt 4 | // which itself was translated from https://web.archive.org/web/20100411050221/http://www.lrz-muenchen.de/~bernhard/Pascal-EBNF.html 5 | // Modifications 6 | // - modified the grammar for compatibility with EbnfStudio 7 | // - renamed type to type_, label to label_, program to program_ and set to set_literal to avoid collision with keyword 8 | // - renamed string to string_literal 9 | // - removed variable_list, output_list, output_value, field_width and fraction_length because unused 10 | // - replaced left recursive variable, component_variable, referenced_variable, indexed_variable, array_variable, 11 | // record_variable, file_variable, pointer_variable, field_designator and file_buffer by a generic 12 | // thus removed entire_variable, component_variable, indexed_variable, referenced_variable, field_designator, etc. 13 | // replaced variable by the variable_reference ::= variable_identifier { qualifier } from the Lisa Pascal spec 14 | // - extended the syntax by the additional constructs from LisaPascal according to 15 | // "Pascal Reference Manual for the Lisa", Beta Draft April 1983 16 | // - correlated the whole syntax with appendix C of the said manual and removed constructs not used by Lisa 17 | // - removed all LL(1) ambiguities 18 | // - Correction of the syntax according to actual Lisa source code (in deviation from the reference manual). Changes: 19 | // added procedure_and_function_interface_part 20 | // files can apparently also only contain procedures 21 | // added non_regular_unit 22 | // NOTE that a preprocessor seems to be required which is the reason why certain files per se don't meet syntax, 23 | // or only meet syntax if conditional sections are actively selected or muted and files are included. 24 | // - Added Clascal syntax from spec; modified as long as compatible with the Lisa source code 25 | 26 | // Programs and Blocks 27 | 28 | #define CLASCAL 29 | 30 | LisaPascal ::= program_ | regular_unit | non_regular_unit 31 | 32 | program_ ::= 33 | program_heading ';' [ uses_clause ] block 34 | [ statement_part '.' ] // can apparently be left out, as e.g. APDM-DESK 35 | program_heading ::= 36 | program identifier [ '(' program_parameters ')' ] 37 | program_parameters ::= identifier_list 38 | uses_clause ::= uses identifier_list2 ';' 39 | identifier_list2 ::= 40 | identifier [ '/' identifier ] { ',' identifier [ '/' identifier ] } // apparently legal, seen e.g. in libhw/keyboard 41 | 42 | regular_unit ::= 43 | unit_heading ';' [ intrinsic [shared] ';' ] // apparently the sources deviate from the spec for intrinsic 44 | interface_part implementation_part 45 | [ end '.' ] // end can apparently be left out, as done in some files 46 | unit_heading ::= 47 | unit identifier 48 | interface_part ::= 49 | interface 50 | [ uses_clause ] 51 | { ( constant_declaration_part 52 | | type_declaration_part 53 | | variable_declaration_part 54 | | procedure_and_function_interface_part ) } 55 | implementation_part ::= 56 | implementation 57 | { ( constant_declaration_part 58 | | type_declaration_part 59 | | variable_declaration_part 60 | #ifdef CLASCAL 61 | | subroutine_part 62 | #else 63 | | procedure_and_function_declaration_part 64 | #endif 65 | ) } 66 | 67 | non_regular_unit ::= 68 | // Coco seems to have an issue with this in that the generated procedure 69 | // without the \LA starts with while( la in {procedure,function,end} 70 | { \LA: 1:(procedure|function)\ procedure_and_function_declaration_part } 71 | [ statement_part '.' | end '.' ] 72 | 73 | block ::= 74 | { ( label_declaration_part 75 | | constant_declaration_part 76 | | type_declaration_part 77 | | variable_declaration_part 78 | | procedure_and_function_declaration_part ) } 79 | // statement_part removed to make it optional if need be 80 | label_declaration_part ::= 81 | label label_ { ',' label_ } ';' 82 | label_ ::= 83 | digit_sequence 84 | constant_declaration_part ::= 85 | const constant_declaration { constant_declaration } 86 | constant_declaration ::= 87 | identifier '=' // constant [';'] // apparently optional, seen e.g. in libte/lcut:46 88 | expression [';'] // apparently this can be a full expression, as e.g. seen in libtk/uabc 89 | constant ::= 90 | [ sign ] ( identifier [ actual_parameter_list ] // constant_identifier or built-in function (apparently supported as well) 91 | | unsigned_number ) | string_literal 92 | type_declaration_part ::= 93 | type type_declaration { type_declaration } 94 | type_declaration ::= 95 | identifier '=' type_ ';' 96 | variable_declaration_part ::= 97 | var variable_declaration { variable_declaration } 98 | variable_declaration ::= 99 | identifier_list ':' type_ ';' 100 | procedure_and_function_interface_part ::= 101 | { procedure_heading ';' | function_heading ';' } 102 | procedure_and_function_declaration_part ::= 103 | { procedure_declaration | function_declaration } 104 | #ifdef CLASCAL 105 | subroutine_part ::= 106 | { procedure_declaration | function_declaration | method_block } 107 | method_block ::= 108 | methods of identifier [';'] // class_identifier 109 | // semi can apparently be left out as seen e.g. in libut/uunivtext 110 | [ procedure_and_function_declaration_part ] 111 | // this is the specified syntax, but apparently nowhere used [ [creation] begin block statement_part ] // creation_block 112 | // creation can apparently be left out, as seen in libqp/uqpgraph 113 | ( statement_part | end ) [';'] // this is the actual syntax used in the code 114 | #endif 115 | procedure_declaration ::= 116 | procedure_heading ';' body_ ';' 117 | body_ ::= // procedure_body, function_body 118 | block statement_part | 'forward' | 'external' | 'inline' constant 119 | function_declaration ::= 120 | function_heading ';' body_ ';' 121 | statement_part ::= 122 | compound_statement 123 | 124 | // Procedure and Function Definitions 125 | 126 | procedure_heading ::= 127 | procedure identifier [ '.' identifier ] [ formal_parameter_list ] 128 | // apparently in method_interface proc and func names can be Class.Name 129 | function_heading ::= 130 | function identifier [ '.' identifier ] [ formal_parameter_list ] 131 | [':' result_type ] // return type can apparently be left out, as done in some files 132 | result_type ::= 133 | type_identifier // ordinal/real/pointer_type_ident 134 | formal_parameter_list ::= 135 | '(' formal_parameter_section { [';'] // semicolon can apparently be optional, seen in libfp-MATHLIB.TEXT 136 | formal_parameter_section } ')' 137 | formal_parameter_section ::= 138 | parameter_declaration | 139 | procedure_heading | 140 | function_heading 141 | parameter_declaration ::= 142 | [var] identifier_list ':' type_identifier // original Pascal in contrast to Lisa also supports conformant_array_schema 143 | 144 | // Statements 145 | 146 | statement_sequence ::= 147 | statement { ';' statement } 148 | statement ::= 149 | [ label_ ':' ] ( [simple_statement] | structured_statement) 150 | simple_statement ::= 151 | // assignment_statement | procedure_statement | goto_statement 152 | assigOrCall | goto_statement 153 | assignment_statement- ::= 154 | (variable_reference | function_identifier) ':=' expression 155 | assigOrCall ::= 156 | variable_reference // covers assig lhs incl. function_identifier 157 | [ ':=' expression ] // covers assig rhs 158 | // no in variable_ref: | [ actual_parameter_list ] ) // covers procedure_statement, but only if lhs has no qualifier 159 | procedure_statement- ::= 160 | procedure_identifier [ actual_parameter_list ] 161 | goto_statement ::= 162 | goto label_ 163 | structured_statement ::= 164 | compound_statement | repetitive_statement | conditional_statement | with_statement 165 | compound_statement ::= 166 | begin statement_sequence end 167 | repetitive_statement ::= 168 | while_statement | repeat_statement | for_statement 169 | while_statement ::= 170 | while expression do statement 171 | repeat_statement ::= 172 | repeat statement_sequence until expression 173 | for_statement ::= 174 | for variable_identifier ':=' initial_value (to | downto) final_value do statement 175 | initial_value ::= 176 | expression 177 | final_value ::= 178 | expression 179 | conditional_statement ::= 180 | if_statement | case_statement 181 | if_statement ::= 182 | if expression then statement [ else statement ] 183 | case_statement ::= 184 | case expression of 185 | [case_limb { \LA: 1: ';' & 2:(!end&!otherwise)\ ';' [case_limb] } ] // apparently stray ';' are allowed in case 186 | // can apparently be empty, as seen e.g. in libtk/uobject:1531 187 | [ \LL:2\ otherwise_clause ] [ ';' ] 188 | end 189 | case_limb ::= 190 | case_label_list ':' statement 191 | case_label_list ::= 192 | constant { ',' constant } 193 | otherwise_clause ::= 194 | [';'] otherwise statement 195 | // semi can apparently be missing, as seen e.g. in libtk/uobject:1531 196 | with_statement ::= 197 | with variable_reference { ',' variable_reference } do statement // references a record 198 | actual_parameter_list ::= 199 | '(' actual_parameter { ',' actual_parameter } ')' 200 | actual_parameter ::= 201 | expression // included | variable_reference | procedure_identifier | function_identifier 202 | 203 | // Expressions 204 | 205 | expression ::= 206 | simple_expression [ relational_operator simple_expression ] 207 | simple_expression ::= 208 | [ sign ] term { addition_operator term } 209 | term ::= 210 | factor { multiplication_operator factor } 211 | factor ::= 212 | '@' variable_reference 213 | | identifier { qualifier | actual_parameter_list } // function_designator, bound_identifier, constant_identifier 214 | // Lisa combines unsigned_number, string, constant_identifier and nil to unsigned_constant 215 | | unsigned_number 216 | | string_literal 217 | | nil 218 | | set_literal 219 | | '(' expression ')' 220 | | not factor 221 | relational_operator ::= 222 | '=' | '<>' | '<' | '<=' | '>' | '>=' | in 223 | addition_operator ::= 224 | '+' | '-' | or 225 | multiplication_operator ::= 226 | '*' | '/' | ':' | div | mod | and // ':' is apparently also supported instead of div 227 | 228 | variable_reference ::= 229 | variable_identifier { qualifier | actual_parameter_list } // apparently lhs can be function call with deref 230 | qualifier ::= 231 | index | field_designator | dereferencer 232 | index ::= 233 | '[' expression_list ']' 234 | field_designator ::= 235 | '.' field_identifier 236 | dereferencer ::= // Lisa: file_buffer_symbol or pointer_object_symbol 237 | '^' 238 | 239 | set_literal ::= // Lisa: set_constructor 240 | '[' [ member_group { ',' member_group } ] ']' 241 | member_group ::= 242 | expression [ '..' expression ] 243 | //function_designator ::= 244 | // function_identifier [ actual_parameter_list ] 245 | 246 | // Types 247 | 248 | type_ ::= 249 | simple_type | string_type | structured_type | pointer_type // already includes ident via simple_type subrange_type: | type_identifier 250 | // ident for real_type_ident, ordinal_type_ident, string_type_ident, structured_type_ident and pointer_type_ident 251 | simple_type ::= 252 | \LA: 1:identifier & 2:!'..'\ identifier 253 | | subrange_type 254 | | enumerated_type 255 | ordinal_type ::= 256 | simple_type // already includes constant_ident via subrange_type: | type_identifier // for ordinal_type_ident 257 | string_type ::= string '[' size_attribute ']' 258 | size_attribute ::= 259 | unsigned_integer 260 | | identifier // apparently also supported 261 | enumerated_type ::= 262 | '(' identifier_list ')' 263 | subrange_type ::= 264 | constant ( '..' | ':' ) constant // ':' apparently is supported too, e.g. in SOURCE-VMSTUFF 265 | structured_type ::= 266 | [ packed ] ( array_type | record_type | set_type | file_type 267 | #ifdef CLASCAL 268 | | class_type 269 | #endif 270 | ) 271 | array_type ::= 272 | array '[' index_type { ',' index_type } ']' of type_ 273 | index_type ::= 274 | ordinal_type 275 | set_type ::= 276 | set of ordinal_type 277 | file_type ::= 278 | file [ of type_ ] // of type_ can apparently be left out, see source/nwshell 279 | pointer_type ::= 280 | '^' type_identifier 281 | 282 | #ifdef CLASCAL 283 | class_type ::= 284 | subclass of ( type_identifier | nil ) // class_identifier; nil apparently supported too, seen in libpl/blkiointr 285 | [ field_list ] 286 | method_interface { method_interface } 287 | end 288 | method_interface ::= 289 | ( procedure_heading | function_heading ) 290 | [ \LL:2\ ';' identifier ] ';' // 'abstract' | 'classwide' | 'override' | 'default' 291 | // override not in spec but used in code 292 | #endif 293 | 294 | // Records 295 | 296 | record_type ::= 297 | record [ field_list ] end 298 | field_list ::= 299 | (fixed_part [ \LL:2\ ';' variant_part ] | variant_part) [ ';' ] 300 | fixed_part ::= 301 | field_declaration { \LL:2\ ';' field_declaration } 302 | field_declaration ::= 303 | identifier_list ':' type_ 304 | variant_part ::= 305 | case [ \LL:2\ tag_field] type_identifier // ordinal_type_ident 306 | of variant { \LA: 1:';' & 2:!(end|')')\ ';' variant } 307 | tag_field ::= 308 | identifier ':' 309 | variant ::= 310 | case_label_list ':' '(' [field_list] ')' 311 | 312 | // Variable and Identifier Categories 313 | 314 | field_identifier ::= 315 | identifier 316 | constant_identifier- ::= 317 | identifier 318 | variable_identifier ::= 319 | identifier 320 | type_identifier ::= 321 | identifier 322 | procedure_identifier- ::= 323 | identifier 324 | function_identifier- ::= 325 | identifier 326 | //bound_identifier ::= 327 | // identifier 328 | 329 | // Low Level Definitions 330 | 331 | identifier ::= // letter { letter | digit | '_' } 332 | // letter ::= 'A' .. 'Z' | 'a' .. 'z' 333 | // digit ::= '0' .. '9' 334 | identifier_list ::= 335 | identifier { ',' identifier } 336 | expression_list ::= 337 | expression { ',' expression } 338 | 339 | unsigned_integer ::= digit_sequence | hex_digit_sequence 340 | unsigned_real ::= // digit_sequence [ '.' digit_sequence ] [ scale_factor ] 341 | // scale_factor ::= ('E' | 'e') [sign] digit_sequence 342 | unsigned_number ::= unsigned_integer | unsigned_real 343 | digit_sequence ::= // digit { digit } 344 | hex_digit_sequence ::= // '$' hex_digit { hex_digit } 345 | // hex_digit ::= digit | 'A'..'F' 346 | sign ::= '+' | '-' 347 | string_literal ::= // ''' string_character { string_character } ''' 348 | 349 | // Helper Tokens 350 | Comment ::= 351 | Directive ::= 352 | comment- ::= '(*' '*)' '{' '}' 353 | #ifndef CLASCAL 354 | oostuff- ::= methods subclass creation 355 | #endif 356 | 357 | // Pragmas 358 | %module ::= 'LisaPascal' 359 | %namespace ::= 'Lisa' 360 | %suppress ::= ';' ',' '.' 361 | 362 | // comaring the syntax of Pascal Manual & Report 2nd (1975) and 4th edition 363 | // - the set of keywords are identical 364 | 365 | // comparing the syntax of LisaPascal 1983 with Manual & Report 366 | // - LisaPascal added these keywords: creation, implementation, interface, intrinsic, methods, otherwise, string 367 | // subclass, unit, uses; no keywords were removed 368 | 369 | // Comparing the syntax of LisaPascal Version 3 with Beta 1983 370 | // - Went through all synatx diagrams in both appendices C 371 | // - All are identical 372 | // - the keywords subclass, methods, creation not used 373 | --------------------------------------------------------------------------------