From 7631b4facea8af54c07db95dedac4e91c94da848 Mon Sep 17 00:00:00 2001 From: Rochus Keller Date: Thu, 11 Apr 2019 15:43:21 +0200 Subject: [PATCH] added CppGen and ObnCpp; integrated CppGen into OberonViewer --- CppKeywordDetector.cpp | 966 +++++++++++++++++++++++++++++++ OBNCPP.pro | 49 ++ ObCppGen.cpp | 1221 ++++++++++++++++++++++++++++++++++++++++ ObCppGen.h | 81 +++ OberonViewer.cpp | 50 ++ OberonViewer.h | 1 + ObnCppMain.cpp | 160 ++++++ ObnViewer.pro | 9 +- ObnViewerMain.cpp | 2 +- README.md | 17 +- _Global-Template.h | 159 ++++++ 11 files changed, 2710 insertions(+), 5 deletions(-) create mode 100644 CppKeywordDetector.cpp create mode 100644 OBNCPP.pro create mode 100644 ObCppGen.cpp create mode 100644 ObCppGen.h create mode 100644 ObnCppMain.cpp create mode 100644 _Global-Template.h diff --git a/CppKeywordDetector.cpp b/CppKeywordDetector.cpp new file mode 100644 index 0000000..073e3ec --- /dev/null +++ b/CppKeywordDetector.cpp @@ -0,0 +1,966 @@ +/* +* Copyright 2019 Rochus Keller +* +* This file is part of the Oberon parser/code model library. +* +* The following is the license that applies to this copy of the +* library. For a license to use the library under conditions +* other than those described here, please email to me@rochus-keller.ch. +* +* GNU General Public License Usage +* This file may be used under the terms of the GNU General Public +* License (GPL) versions 2.0 or 3.0 as published by the Free Software +* Foundation and appearing in the file LICENSE.GPL included in +* the packaging of this file. Please review the following information +* to ensure GNU General Public Licensing requirements will be met: +* http://www.fsf.org/licensing/licenses/info/GPLv2.html and +* http://www.gnu.org/copyleft/gpl.html. +*/ + +#include + +static inline char at( const QByteArray& str, int i ){ + return ( i >= 0 && i < str.size() ? str[i] : 0 ); +} + +// Generated with EbnfStudio and manually modified +bool isCppKeyword( const QByteArray& str ) { + int i = 0; + bool res = false; + switch( at(str,i) ){ + case 'a': + switch( at(str,i+1) ){ + case 'l': + if( at(str,i+2) == 'i' ){ + if( at(str,i+3) == 'g' ){ + if( at(str,i+4) == 'n' ){ + switch( at(str,i+5) ){ + case 'a': + if( at(str,i+6) == 's' ){ + res = true; i += 7; + } + break; + case 'o': + if( at(str,i+6) == 'f' ){ + res = true; i += 7; + } + break; + } + } + } + } + break; + case 'n': + if( at(str,i+2) == 'd' ){ + if( at(str,i+3) == '_' ){ + if( at(str,i+4) == 'e' ){ + if( at(str,i+5) == 'q' ){ + res = true; i += 6; + } + } + } else { + res = true; i += 3; + } + } + break; + case 's': + if( at(str,i+2) == 'm' ){ + res = true; i += 3; + } + break; + case 'u': + if( at(str,i+2) == 't' ){ + if( at(str,i+3) == 'o' ){ + res = true; i += 4; + } + } + break; + } + break; + case 'b': + switch( at(str,i+1) ){ + case 'i': + if( at(str,i+2) == 't' ){ + switch( at(str,i+3) ){ + case 'a': + if( at(str,i+4) == 'n' ){ + if( at(str,i+5) == 'd' ){ + res = true; i += 6; + } + } + break; + case 'o': + if( at(str,i+4) == 'r' ){ + res = true; i += 5; + } + break; + } + } + break; + case 'o': + if( at(str,i+2) == 'o' ){ + if( at(str,i+3) == 'l' ){ + res = true; i += 4; + } + } + break; + case 'r': + if( at(str,i+2) == 'e' ){ + if( at(str,i+3) == 'a' ){ + if( at(str,i+4) == 'k' ){ + res = true; i += 5; + } + } + } + break; + } + break; + case 'c': + switch( at(str,i+1) ){ + case 'a': + switch( at(str,i+2) ){ + case 's': + if( at(str,i+3) == 'e' ){ + res = true; i += 4; + } + break; + case 't': + if( at(str,i+3) == 'c' ){ + if( at(str,i+4) == 'h' ){ + res = true; i += 5; + } + } + break; + } + break; + case 'h': + if( at(str,i+2) == 'a' ){ + if( at(str,i+3) == 'r' ){ + switch( at(str,i+4) ){ + case '1': + if( at(str,i+5) == '6' ){ + if( at(str,i+6) == '_' ){ + if( at(str,i+7) == 't' ){ + res = true; i += 8; + } + } + } + break; + case '3': + if( at(str,i+5) == '2' ){ + if( at(str,i+6) == '_' ){ + if( at(str,i+7) == 't' ){ + res = true; i += 8; + } + } + } + break; + default: + res = true; i += 4; + break; + } + } + } + break; + case 'l': + if( at(str,i+2) == 'a' ){ + if( at(str,i+3) == 's' ){ + if( at(str,i+4) == 's' ){ + res = true; i += 5; + } + } + } + break; + case 'o': + switch( at(str,i+2) ){ + case 'm': + if( at(str,i+3) == 'p' ){ + if( at(str,i+4) == 'l' ){ + res = true; i += 5; + } + } + break; + case 'n': + switch( at(str,i+3) ){ + case 's': + if( at(str,i+4) == 't' ){ + switch( at(str,i+5) ){ + case '_': + if( at(str,i+6) == 'c' ){ + if( at(str,i+7) == 'a' ){ + if( at(str,i+8) == 's' ){ + if( at(str,i+9) == 't' ){ + res = true; i += 10; + } + } + } + } + break; + case 'e': + if( at(str,i+6) == 'x' ){ + if( at(str,i+7) == 'p' ){ + if( at(str,i+8) == 'r' ){ + res = true; i += 9; + } + } + } + break; + default: + res = true; i += 5; + break; + } + } + break; + case 't': + if( at(str,i+4) == 'i' ){ + if( at(str,i+5) == 'n' ){ + if( at(str,i+6) == 'u' ){ + if( at(str,i+7) == 'e' ){ + res = true; i += 8; + } + } + } + } + break; + } + break; + } + break; + } + break; + case 'd': + switch( at(str,i+1) ){ + case 'e': + switch( at(str,i+2) ){ + case 'c': + if( at(str,i+3) == 'l' ){ + if( at(str,i+4) == 't' ){ + if( at(str,i+5) == 'y' ){ + if( at(str,i+6) == 'p' ){ + if( at(str,i+7) == 'e' ){ + res = true; i += 8; + } + } + } + } + } + break; + case 'f': + if( at(str,i+3) == 'a' ){ + if( at(str,i+4) == 'u' ){ + if( at(str,i+5) == 'l' ){ + if( at(str,i+6) == 't' ){ + res = true; i += 7; + } + } + } + } + break; + case 'l': + if( at(str,i+3) == 'e' ){ + if( at(str,i+4) == 't' ){ + if( at(str,i+5) == 'e' ){ + res = true; i += 6; + } + } + } + break; + } + break; + case 'o': + if( at(str,i+2) == 'u' ){ + if( at(str,i+3) == 'b' ){ + if( at(str,i+4) == 'l' ){ + if( at(str,i+5) == 'e' ){ + res = true; i += 6; + } + } + } + } else { + res = true; i += 2; + } + break; + case 'y': + if( at(str,i+2) == 'n' ){ + if( at(str,i+3) == 'a' ){ + if( at(str,i+4) == 'm' ){ + if( at(str,i+5) == 'i' ){ + if( at(str,i+6) == 'c' ){ + if( at(str,i+7) == '_' ){ + if( at(str,i+8) == 'c' ){ + if( at(str,i+9) == 'a' ){ + if( at(str,i+10) == 's' ){ + if( at(str,i+11) == 't' ){ + res = true; i += 12; + } + } + } + } + } + } + } + } + } + } + break; + } + break; + case 'e': + switch( at(str,i+1) ){ + case 'l': + if( at(str,i+2) == 's' ){ + if( at(str,i+3) == 'e' ){ + res = true; i += 4; + } + } + break; + case 'n': + if( at(str,i+2) == 'u' ){ + if( at(str,i+3) == 'm' ){ + res = true; i += 4; + } + } + break; + case 'x': + switch( at(str,i+2) ){ + case 'p': + switch( at(str,i+3) ){ + case 'l': + if( at(str,i+4) == 'i' ){ + if( at(str,i+5) == 'c' ){ + if( at(str,i+6) == 'i' ){ + if( at(str,i+7) == 't' ){ + res = true; i += 8; + } + } + } + } + break; + case 'o': + if( at(str,i+4) == 'r' ){ + if( at(str,i+5) == 't' ){ + res = true; i += 6; + } + } + break; + } + break; + case 't': + if( at(str,i+3) == 'e' ){ + if( at(str,i+4) == 'r' ){ + if( at(str,i+5) == 'n' ){ + res = true; i += 6; + } + } + } + break; + } + break; + } + break; + case 'f': + switch( at(str,i+1) ){ + case 'a': + if( at(str,i+2) == 'l' ){ + if( at(str,i+3) == 's' ){ + if( at(str,i+4) == 'e' ){ + res = true; i += 5; + } + } + } + break; + case 'l': + if( at(str,i+2) == 'o' ){ + if( at(str,i+3) == 'a' ){ + if( at(str,i+4) == 't' ){ + res = true; i += 5; + } + } + } + break; + case 'o': + if( at(str,i+2) == 'r' ){ + res = true; i += 3; + } + break; + case 'r': + if( at(str,i+2) == 'i' ){ + if( at(str,i+3) == 'e' ){ + if( at(str,i+4) == 'n' ){ + if( at(str,i+5) == 'd' ){ + res = true; i += 6; + } + } + } + } + break; + } + break; + case 'g': + if( at(str,i+1) == 'o' ){ + if( at(str,i+2) == 't' ){ + if( at(str,i+3) == 'o' ){ + res = true; i += 4; + } + } + } + break; + case 'i': + switch( at(str,i+1) ){ + case 'f': + res = true; i += 2; + break; + case 'n': + switch( at(str,i+2) ){ + case 'l': + if( at(str,i+3) == 'i' ){ + if( at(str,i+4) == 'n' ){ + if( at(str,i+5) == 'e' ){ + res = true; i += 6; + } + } + } + break; + case 't': + res = true; i += 3; + break; + } + break; + } + break; + case 'l': + if( at(str,i+1) == 'o' ){ + if( at(str,i+2) == 'n' ){ + if( at(str,i+3) == 'g' ){ + res = true; i += 4; + } + } + } + break; + case 'm': + if( at(str,i+1) == 'u' ){ + if( at(str,i+2) == 't' ){ + if( at(str,i+3) == 'a' ){ + if( at(str,i+4) == 'b' ){ + if( at(str,i+5) == 'l' ){ + if( at(str,i+6) == 'e' ){ + res = true; i += 7; + } + } + } + } + } + } + break; + case 'n': + switch( at(str,i+1) ){ + case 'a': + if( at(str,i+2) == 'm' ){ + if( at(str,i+3) == 'e' ){ + if( at(str,i+4) == 's' ){ + if( at(str,i+5) == 'p' ){ + if( at(str,i+6) == 'a' ){ + if( at(str,i+7) == 'c' ){ + if( at(str,i+8) == 'e' ){ + res = true; i += 9; + } + } + } + } + } + } + } + break; + case 'e': + if( at(str,i+2) == 'w' ){ + res = true; i += 3; + } + break; + case 'o': + switch( at(str,i+2) ){ + case 'e': + if( at(str,i+3) == 'x' ){ + if( at(str,i+4) == 'c' ){ + if( at(str,i+5) == 'e' ){ + if( at(str,i+6) == 'p' ){ + if( at(str,i+7) == 't' ){ + res = true; i += 8; + } + } + } + } + } + break; + case 't': + if( at(str,i+3) == '_' ){ + if( at(str,i+4) == 'e' ){ + if( at(str,i+5) == 'q' ){ + res = true; i += 6; + } + } + } else { + res = true; i += 3; + } + break; + } + break; + case 'u': + if( at(str,i+2) == 'l' ){ + if( at(str,i+3) == 'l' ){ + if( at(str,i+4) == 'p' ){ + if( at(str,i+5) == 't' ){ + if( at(str,i+6) == 'r' ){ + res = true; i += 7; + } + } + } + } + } + break; + } + break; + case 'o': + switch( at(str,i+1) ){ + case 'p': + if( at(str,i+2) == 'e' ){ + if( at(str,i+3) == 'r' ){ + if( at(str,i+4) == 'a' ){ + if( at(str,i+5) == 't' ){ + if( at(str,i+6) == 'o' ){ + if( at(str,i+7) == 'r' ){ + res = true; i += 8; + } + } + } + } + } + } + break; + case 'r': + if( at(str,i+2) == '_' ){ + if( at(str,i+3) == 'e' ){ + if( at(str,i+4) == 'q' ){ + res = true; i += 5; + } + } + } else { + res = true; i += 2; + } + break; + } + break; + case 'p': + switch( at(str,i+1) ){ + case 'r': + switch( at(str,i+2) ){ + case 'i': + if( at(str,i+3) == 'v' ){ + if( at(str,i+4) == 'a' ){ + if( at(str,i+5) == 't' ){ + if( at(str,i+6) == 'e' ){ + res = true; i += 7; + } + } + } + } + break; + case 'o': + if( at(str,i+3) == 't' ){ + if( at(str,i+4) == 'e' ){ + if( at(str,i+5) == 'c' ){ + if( at(str,i+6) == 't' ){ + if( at(str,i+7) == 'e' ){ + if( at(str,i+8) == 'd' ){ + res = true; i += 9; + } + } + } + } + } + } + break; + } + break; + case 'u': + if( at(str,i+2) == 'b' ){ + if( at(str,i+3) == 'l' ){ + if( at(str,i+4) == 'i' ){ + if( at(str,i+5) == 'c' ){ + res = true; i += 6; + } + } + } + } + break; + } + break; + case 'r': + if( at(str,i+1) == 'e' ){ + switch( at(str,i+2) ){ + case 'g': + if( at(str,i+3) == 'i' ){ + if( at(str,i+4) == 's' ){ + if( at(str,i+5) == 't' ){ + if( at(str,i+6) == 'e' ){ + if( at(str,i+7) == 'r' ){ + res = true; i += 8; + } + } + } + } + } + break; + case 'i': + if( at(str,i+3) == 'n' ){ + if( at(str,i+4) == 't' ){ + if( at(str,i+5) == 'e' ){ + if( at(str,i+6) == 'r' ){ + if( at(str,i+7) == 'p' ){ + if( at(str,i+8) == 'r' ){ + if( at(str,i+9) == 'e' ){ + if( at(str,i+10) == 't' ){ + if( at(str,i+11) == '_' ){ + if( at(str,i+12) == 'c' ){ + if( at(str,i+13) == 'a' ){ + if( at(str,i+14) == 's' ){ + if( at(str,i+15) == 't' ){ + res = true; i += 16; + } + } + } + } + } + } + } + } + } + } + } + } + } + break; + case 't': + if( at(str,i+3) == 'u' ){ + if( at(str,i+4) == 'r' ){ + if( at(str,i+5) == 'n' ){ + res = true; i += 6; + } + } + } + break; + } + } + break; + case 's': + switch( at(str,i+1) ){ + case 'h': + if( at(str,i+2) == 'o' ){ + if( at(str,i+3) == 'r' ){ + if( at(str,i+4) == 't' ){ + res = true; i += 5; + } + } + } + break; + case 'i': + switch( at(str,i+2) ){ + case 'g': + if( at(str,i+3) == 'n' ){ + if( at(str,i+4) == 'e' ){ + if( at(str,i+5) == 'd' ){ + res = true; i += 6; + } + } + } + break; + case 'z': + if( at(str,i+3) == 'e' ){ + if( at(str,i+4) == 'o' ){ + if( at(str,i+5) == 'f' ){ + res = true; i += 6; + } + } + } + break; + } + break; + case 't': + switch( at(str,i+2) ){ + case 'a': + if( at(str,i+3) == 't' ){ + if( at(str,i+4) == 'i' ){ + if( at(str,i+5) == 'c' ){ + if( at(str,i+6) == '_' ){ + switch( at(str,i+7) ){ + case 'a': + if( at(str,i+8) == 's' ){ + if( at(str,i+9) == 's' ){ + if( at(str,i+10) == 'e' ){ + if( at(str,i+11) == 'r' ){ + if( at(str,i+12) == 't' ){ + res = true; i += 13; + } + } + } + } + } + break; + case 'c': + if( at(str,i+8) == 'a' ){ + if( at(str,i+9) == 's' ){ + if( at(str,i+10) == 't' ){ + res = true; i += 11; + } + } + } + break; + } + } else { + res = true; i += 6; + } + } + } + } + break; + case 'r': + if( at(str,i+3) == 'u' ){ + if( at(str,i+4) == 'c' ){ + if( at(str,i+5) == 't' ){ + res = true; i += 6; + } + } + } + break; + } + break; + case 'w': + if( at(str,i+2) == 'i' ){ + if( at(str,i+3) == 't' ){ + if( at(str,i+4) == 'c' ){ + if( at(str,i+5) == 'h' ){ + res = true; i += 6; + } + } + } + } + break; + } + break; + case 't': + switch( at(str,i+1) ){ + case 'e': + if( at(str,i+2) == 'm' ){ + if( at(str,i+3) == 'p' ){ + if( at(str,i+4) == 'l' ){ + if( at(str,i+5) == 'a' ){ + if( at(str,i+6) == 't' ){ + if( at(str,i+7) == 'e' ){ + res = true; i += 8; + } + } + } + } + } + } + break; + case 'h': + switch( at(str,i+2) ){ + case 'i': + if( at(str,i+3) == 's' ){ + res = true; i += 4; + } + break; + case 'r': + switch( at(str,i+3) ){ + case 'e': + if( at(str,i+4) == 'a' ){ + if( at(str,i+5) == 'd' ){ + if( at(str,i+6) == '_' ){ + if( at(str,i+7) == 'l' ){ + if( at(str,i+8) == 'o' ){ + if( at(str,i+9) == 'c' ){ + if( at(str,i+10) == 'a' ){ + if( at(str,i+11) == 'l' ){ + res = true; i += 12; + } + } + } + } + } + } + } + } + break; + case 'o': + if( at(str,i+4) == 'w' ){ + res = true; i += 5; + } + break; + } + break; + } + break; + case 'r': + switch( at(str,i+2) ){ + case 'u': + if( at(str,i+3) == 'e' ){ + res = true; i += 4; + } + break; + case 'y': + res = true; i += 3; + break; + } + break; + case 'y': + if( at(str,i+2) == 'p' ){ + if( at(str,i+3) == 'e' ){ + switch( at(str,i+4) ){ + case 'd': + if( at(str,i+5) == 'e' ){ + if( at(str,i+6) == 'f' ){ + res = true; i += 7; + } + } + break; + case 'i': + if( at(str,i+5) == 'd' ){ + res = true; i += 6; + } + break; + case 'n': + if( at(str,i+5) == 'a' ){ + if( at(str,i+6) == 'm' ){ + if( at(str,i+7) == 'e' ){ + res = true; i += 8; + } + } + } + break; + } + } + } + break; + } + break; + case 'u': + switch( at(str,i+1) ){ + case 'n': + switch( at(str,i+2) ){ + case 'i': + if( at(str,i+3) == 'o' ){ + if( at(str,i+4) == 'n' ){ + res = true; i += 5; + } + } + break; + case 's': + if( at(str,i+3) == 'i' ){ + if( at(str,i+4) == 'g' ){ + if( at(str,i+5) == 'n' ){ + if( at(str,i+6) == 'e' ){ + if( at(str,i+7) == 'd' ){ + res = true; i += 8; + } + } + } + } + } + break; + } + break; + case 's': + if( at(str,i+2) == 'i' ){ + if( at(str,i+3) == 'n' ){ + if( at(str,i+4) == 'g' ){ + res = true; i += 5; + } + } + } + break; + } + break; + case 'v': + switch( at(str,i+1) ){ + case 'i': + if( at(str,i+2) == 'r' ){ + if( at(str,i+3) == 't' ){ + if( at(str,i+4) == 'u' ){ + if( at(str,i+5) == 'a' ){ + if( at(str,i+6) == 'l' ){ + res = true; i += 7; + } + } + } + } + } + break; + case 'o': + switch( at(str,i+2) ){ + case 'i': + if( at(str,i+3) == 'd' ){ + res = true; i += 4; + } + break; + case 'l': + if( at(str,i+3) == 'a' ){ + if( at(str,i+4) == 't' ){ + if( at(str,i+5) == 'i' ){ + if( at(str,i+6) == 'l' ){ + if( at(str,i+7) == 'e' ){ + res = true; i += 8; + } + } + } + } + } + break; + } + break; + } + break; + case 'w': + switch( at(str,i+1) ){ + case 'c': + if( at(str,i+2) == 'h' ){ + if( at(str,i+3) == 'a' ){ + if( at(str,i+4) == 'r' ){ + if( at(str,i+5) == '_' ){ + if( at(str,i+6) == 't' ){ + res = true; i += 7; + } + } + } + } + } + break; + case 'h': + if( at(str,i+2) == 'i' ){ + if( at(str,i+3) == 'l' ){ + if( at(str,i+4) == 'e' ){ + res = true; i += 5; + } + } + } + break; + } + break; + case 'x': + if( at(str,i+1) == 'o' ){ + if( at(str,i+2) == 'r' ){ + if( at(str,i+3) == '_' ){ + if( at(str,i+4) == 'e' ){ + if( at(str,i+5) == 'q' ){ + res = true; i += 6; + } + } + } else { + res = true; i += 3; + } + } + } + break; + } + return res; +} + diff --git a/OBNCPP.pro b/OBNCPP.pro new file mode 100644 index 0000000..49c5dce --- /dev/null +++ b/OBNCPP.pro @@ -0,0 +1,49 @@ +#/* +#* Copyright 2019 Rochus Keller +#* +#* This file is part of the Oberon parser/code model library. +#* +#* The following is the license that applies to this copy of the +#* library. For a license to use the library under conditions +#* other than those described here, please email to me@rochus-keller.ch. +#* +#* GNU General Public License Usage +#* This file may be used under the terms of the GNU General Public +#* License (GPL) versions 2.0 or 3.0 as published by the Free Software +#* Foundation and appearing in the file LICENSE.GPL included in +#* the packaging of this file. Please review the following information +#* to ensure GNU General Public Licensing requirements will be met: +#* http://www.fsf.org/licensing/licenses/info/GPLv2.html and +#* http://www.gnu.org/copyleft/gpl.html. +#*/ + +QT += core +QT -= gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = OberonTest +TEMPLATE = app + +INCLUDEPATH += .. + +SOURCES += \ + ObnCppMain.cpp \ + ObCppGen.cpp \ + CppKeywordDetector.cpp + +HEADERS += \ + ObCppGen.h + +include( Oberon.pri ) + +CONFIG(debug, debug|release) { + DEFINES += _DEBUG +} + +QMAKE_CXXFLAGS += -Wno-reorder -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable + + + + + diff --git a/ObCppGen.cpp b/ObCppGen.cpp new file mode 100644 index 0000000..0ec32da --- /dev/null +++ b/ObCppGen.cpp @@ -0,0 +1,1221 @@ +/* +* Copyright 2019 Rochus Keller +* +* This file is part of the Oberon parser/code model library. +* +* The following is the license that applies to this copy of the +* library. For a license to use the library under conditions +* other than those described here, please email to me@rochus-keller.ch. +* +* GNU General Public License Usage +* This file may be used under the terms of the GNU General Public +* License (GPL) versions 2.0 or 3.0 as published by the Free Software +* Foundation and appearing in the file LICENSE.GPL included in +* the packaging of this file. Please review the following information +* to ensure GNU General Public Licensing requirements will be met: +* http://www.fsf.org/licensing/licenses/info/GPLv2.html and +* http://www.gnu.org/copyleft/gpl.html. +*/ + +#include "ObCppGen.h" +#include +#include +#include +#include +#include +#include +using namespace Ob; + +bool isCppKeyword( const QByteArray& str ); // aus CppKeywordDetector.cpp + +static inline const CodeModel::Type* derefed( const CodeModel::Type* t ) +{ + return ( t != 0 ? t->deref() : 0 ); +} + +static QByteArray quali( const SynTree* st ) +{ + Q_ASSERT( st != 0 && st->d_tok.d_type == SynTree::R_qualident ); + if( st->d_children.size() == 1 ) + return st->d_children.first()->d_tok.d_val; + else + return st->d_children.first()->d_tok.d_val + "::" + st->d_children.last()->d_tok.d_val; +} + +CppGen::CppGen(CodeModel* mdl):d_mdl(mdl),d_errs(0),d_genStubs(true) +{ + Q_ASSERT( mdl != 0 ); + d_errs = mdl->getErrs(); +} + +bool CppGen::emitModules(const QString& outdir, const QString& ns, const QString& mod) +{ + d_ns = ns; + d_outdir = outdir; + d_mod = mod; + d_nameNr = 1; + const int precount = d_errs->getErrCount(); + foreach( CodeModel::Module* m, d_mdl->getGlobalScope().d_mods ) + { + qDebug() << "translating module" << m->d_name; + emitModule(m); + } + return precount == int(d_errs->getErrCount()); +} + +void CppGen::emitModule(const CodeModel::Module* m) +{ + QDir dir(d_outdir); + if( !d_mod.isEmpty() ) + { + dir.mkpath( d_mod ); + dir.cd( d_mod ); + } + QFile hfile( dir.absoluteFilePath( d_ns + m->d_name + ".h" )); + if( !hfile.open(QIODevice::WriteOnly ) ) + { + d_errs->error(Errors::Generator,m->d_name, 0,0,QString("cannot open file '%1' for writing").arg(hfile.fileName()) ); + return; + } + + QTextStream hout(&hfile); + + const QString guard = QString("__%1%2_H__").arg(d_ns).arg(m->d_name.data()); + hout << "#ifndef " << guard << endl; + hout << "#define " << guard << endl << endl; + + hout << "// Generated by " << qApp->applicationName() << " " << qApp->applicationVersion() << + " on " << QDateTime::currentDateTime().toString(Qt::ISODate) << endl << endl; + + hout << "#include <" << includeFileName( "_Global" ) << ".h>" << endl; + foreach( CodeModel::Module* dep, m->d_using ) + hout << "#include <" << includeFileName( dep->d_name ) << ".h>" << endl; + hout << endl; + + int lh = 0; + if( !d_ns.isEmpty() ) + { + hout << "namespace " << d_ns << endl << "{" << endl; + lh++; + } + + d_cmts.clear(); + if( m->d_def ) + d_cmts = d_mdl->getComments(m->d_def->d_tok.d_sourcePath); + d_nextCmt = 0; + + emitHeader(m,hout,lh); + + if( !d_ns.isEmpty() ) + hout << "}" << endl << endl; + + hout << "#endif // " << guard; + + + if( m->d_def != 0 ) + { + QFile bfile( dir.absoluteFilePath( d_ns + m->d_name + ".cpp" )); + if( !bfile.open(QIODevice::WriteOnly ) ) + { + d_errs->error(Errors::Generator,m->d_name, 0,0,QString("cannot open file '%1' for writing").arg(bfile.fileName()) ); + return; + } + + QTextStream bout(&bfile); + + bout << "// Generated by " << qApp->applicationName() << " " << qApp->applicationVersion() << + " on " << QDateTime::currentDateTime().toString(Qt::ISODate) << endl; + bout << "#include \"" << d_ns << m->d_name << ".h\"" << endl; + bout << "#include " << endl; + if( !d_ns.isEmpty() ) + bout << "using namespace " << d_ns << ";" << endl << endl; + + emitBody(m,bout); + } +} + +void CppGen::emitHeader(const CodeModel::Module* m, QTextStream& out, int l) +{ + out << ws(l) << "class " << escape(m->d_name) << " : public _Root" << endl << + ws(l) << "{" << endl << + ws(l) << "public:" << endl; + l++; + emitComment(m->d_def,out,l); + out << ws(l) << "static " << escape(m->d_name) << "* _inst();" << endl; + out << ws(l) << escape(m->d_name) << "();" << endl; + out << ws(l) << "~" << escape(m->d_name) << "();" << endl << endl; + + emitDecls(m,out,l); + + if( !m->d_procs.isEmpty() ) + out << ws(l) << "// PROC" << endl; + foreach( const CodeModel::Procedure* v, m->d_procs ) + { + emitProcDecl(v,out,l); + } + if( !m->d_procs.isEmpty() ) + out << endl; + + + l--; + out << ws(l) << "};" << endl; +} + +void CppGen::emitBody(const CodeModel::Module* m, QTextStream& out ) +{ + out << "static std::auto_ptr<" << escape(m->d_name) << "> s_inst;" << endl << endl; + + foreach( const CodeModel::Element* c, m->getConsts() ) + { + out << "const "; + const CodeModel::Type* type = d_mdl->typeOfExpression(m,c->d_st); + emitType( m, type, out, 0 ); + out << " " << escape(m->d_name) << "::" << escape(c->d_name) << ";" << endl; + } + out << endl; + + out << escape(m->d_name) << "* " << escape(m->d_name) << "::_inst()" << endl << "{" << endl; + out << ws(1) << "if( s_inst.get() == 0 )" << endl; + out << ws(2) << "s_inst.reset( new " << escape(m->d_name) << "() );" << endl; + out << ws(1) << "return s_inst.get();" << endl; + out << "}" << endl << endl; + + foreach( const CodeModel::Procedure* v, m->d_procs ) + { + emitProcBody(v,out); + } + + // Constructor + out << escape(m->d_name) << "::" << escape(m->d_name) << "()" << endl << "{" << endl; + if( !m->d_body.isEmpty() ) + out << ws(1) << "// BEGIN" << endl; + emitStatementSeq(m, m->d_body, out,1); + if( !m->d_body.isEmpty() ) + out << ws(1) << "// END" << endl; + out << "}" << endl << endl; + + // Destructor + out << escape(m->d_name) << "::~" << escape(m->d_name) << "()" << endl << "{" << endl; + out << ws(1) << "s_inst.release();" << endl; + out << "}" << endl << endl; + +} + +QString CppGen::includeFileName(const QString& modName) +{ + QString path; + if( !d_mod.isEmpty() ) + path = d_mod + "/"; + if( !d_ns.isEmpty() ) + path += d_ns; + path += modName; + return path; +} + +QString CppGen::ws(int level) +{ + return QString(level,QChar('\t')); +} + +QString CppGen::basicType(const CodeModel::Type* t) +{ + if( t == 0 ) + return "int /* unknown type */"; + switch( t->d_kind ) + { + case CodeModel::Type::BOOLEAN: + return "bool"; + case CodeModel::Type::CHAR: + return "char"; + case CodeModel::Type::INTEGER: + return "int"; + case CodeModel::Type::REAL: + return "float"; + case CodeModel::Type::BYTE: + return "uint8_t"; + case CodeModel::Type::SET: + return "_Set"; + case CodeModel::Type::STRING: + return "_ValArray"; + case CodeModel::Type::NIL: + return "void*"; + default: + break; + } + Q_ASSERT( false ); + return QString(); +} + +void CppGen::emitType(const CodeModel::Unit* ds,const CodeModel::Type* t, QTextStream& out, int level ) +{ + if( t != 0 ) + { + if( t->isBasicType() ) + out << basicType(t); + else if( t->d_kind == CodeModel::Type::TypeRef ) + { + const CodeModel::Type* b = t->d_type; + if( b && b->isBasicType() ) + out << basicType(b); + else + { + CodeModel::Quali q = d_mdl->derefQualident( ds, t->d_st ); + if( q.second.first ) + { + if( q.first.first ) + out << escape(q.first.first->d_name) << "::"; + if( q.second.first ) + out << escape(q.second.first->d_name); + }else + out << "Unknown"; + } + }else if( t->d_kind == CodeModel::Type::Array ) + { + Q_ASSERT( t->d_type->d_name.isEmpty() ); + if( t && ( t->d_type->d_kind == CodeModel::Type::TypeRef || t->d_type->d_kind == CodeModel::Type::Array ) ) + { + out << "_FxArray<"; + emitType(ds, t->d_type, out, level ); + }else + { + qDebug() << "Array Type" << SynTree::rToStr(t->d_type->d_def->d_tok.d_type); + // Erzeuge Namen und füge Type Decl ein + const QByteArray name = "_Type" + QByteArray::number(d_nameNr++); + out << "_FxArray<"; + out << name; + + } + if( t->d_st ) + { + out << ","; + emitExpression(ds,t->d_st,out,level); + }else + out << ",0"; +// if( t->d_type && t->d_type->d_kind == CodeModel::Type::Array ) +// out << " "; + out << ">"; + }else if( t->d_kind == CodeModel::Type::Record ) + emitRecDecl(ds,t,QByteArray(),out,level); + else if( t->d_kind == CodeModel::Type::Pointer ) + { + emitType(ds,t->d_type,out,level); + out << "*"; + } + else if( t->d_kind == CodeModel::Type::ProcRef ) + Q_ASSERT_X(false,"generateTypeDecl","ProcType not supported here"); + else + out << "int /* unknown type */"; + }else + out << "int /* unknown type */"; +} + +void CppGen::emitProcType(const CodeModel::Unit* ds, const CodeModel::Type* p, const QByteArray& name, QTextStream& out, int level) +{ + Q_ASSERT( p->d_kind == CodeModel::Type::ProcRef ); + if( p->d_type == 0 ) + out << "void "; + else + { + emitType(ds,p->d_type,out,level); + out << " "; + } + out << "(*" << name << ")("; + CodeModel::Type::Vals::const_iterator i; + for( i = p->d_vals.begin(); i != p->d_vals.end(); ++i ) + { + if( i != p->d_vals.begin() ) + out << ","; + const CodeModel::Element* par = i.value(); + emitType(ds,par->d_type,out,level); + if( par->d_var ) + out << "&"; + } + out << ")"; +} + +void CppGen::emitFactor(const CodeModel::Unit* ds,const SynTree* st, QTextStream& out, int level ) +{ + Q_ASSERT( st != 0 && st->d_tok.d_type == SynTree::R_factor && !st->d_children.isEmpty() ); + const SynTree* first = st->d_children.first(); + switch( first->d_tok.d_type ) + { + case SynTree::R_set: + emitSet(ds,first,out, level); + break; + case SynTree::R_number: + Q_ASSERT( !st->d_children.first()->d_children.isEmpty() ); + if( st->d_children.first()->d_children.first()->d_tok.d_val.endsWith('H') ) + out << "0x" << st->d_children.first()->d_children.first()->d_tok.d_val.left( + st->d_children.first()->d_children.first()->d_tok.d_val.size() - 1 ); + else + out << st->d_children.first()->d_children.first()->d_tok.d_val; + break; + case Tok_TRUE: + out << "true"; + break; + case Tok_FALSE: + out << "false"; + break; + case Tok_NIL: + out << "0"; + break; + case Tok_Lpar: + out << "("; + Q_ASSERT(st->d_children.size() == 3); + emitExpression(ds,st->d_children[1],out,level); + out << ")"; + break; + case Tok_Tilde: + out << "!"; + Q_ASSERT(st->d_children.size() == 2 ); + emitFactor(ds,st->d_children[1], out, level ); + break; + case Tok_string: + if( first->d_tok.d_val.size() == 3 ) + out << "'" << ( first->d_tok.d_val[1] == '\'' ? "\\" : "" ) << first->d_tok.d_val[1] << "'"; // CHAR + else + out << first->d_tok.d_val; + break; + case Tok_hexchar: + out << "0x" << first->d_tok.d_val.left(first->d_tok.d_val.size() - 1 ); + break; + case Tok_hexstring: + out << "\"" << first->d_tok.d_val << "\""; + break; + case SynTree::R_variableOrFunctionCall: + emitDesig(ds,first->d_children.first(), false, out,level); + break; + default: + Q_ASSERT( false ); + break; + } +} + +bool CppGen::emitDesig(const CodeModel::Unit* ds, const SynTree* st, bool procCall, QTextStream& out, int level) +{ + Q_ASSERT( st != 0 && st->d_tok.d_type == SynTree::R_designator ); + CodeModel::DesigOpList dopl = d_mdl->derefDesignator(ds,st); + + bool printedSomething = false; + bool lastIsPointer = false; + for( int i = 0; i < dopl.size(); i++ ) + { + switch( dopl[i].d_op ) + { + case CodeModel::IdentOp: + Q_ASSERT( dopl[i].d_sym ); + if( i == 0 && dynamic_cast(dopl[i].d_sym) ) + { + out << escape(dopl[i].d_sym->d_name) + "::_inst()"; + lastIsPointer = true; + printedSomething = true; + }else + { + const CodeModel::Element* e = dynamic_cast( dopl[i].d_sym ); + if( e && e->isPredefProc() ) + { + if( emitPredefProc( ds, dopl, out, level ) ) + return true; + else + out << escape(dopl[i].d_sym->d_name); + }else + { + if( i != 0 ) + { + if( lastIsPointer ) + out << "->"; + else + out << "."; + lastIsPointer = false; + } + + if( i == 0 && dynamic_cast(ds) == 0 // wir sind also nicht im Constructor + && dynamic_cast(dopl[i].d_sym->d_scope) != 0 // es ist also eine Modulvariable + ) + out << "_this->"; + out << escape(dopl[i].d_sym->d_name); + if( e && e->d_kind == CodeModel::Element::Variable ) + { + const CodeModel::Type* t = derefed(e->d_type); + lastIsPointer = ( t != 0 && t->d_kind == CodeModel::Type::Pointer ); + } + } + } + break; + case CodeModel::PointerOp: + lastIsPointer = true; + break; + case CodeModel::TypeOp: + { + if( lastIsPointer ) + out << "->"; + else + out << "."; + out << "_to<" << quali(dopl[i].d_arg) << ">()"; + lastIsPointer = false; + const CodeModel::Type* t = derefed(dynamic_cast(dopl[i].d_sym)); + if( t && t->d_kind == CodeModel::Type::Pointer ) + lastIsPointer = true; + } + break; + case CodeModel::ProcedureOp: + out << "("; + if( dopl[i].d_arg ) + { + Q_ASSERT( dopl[i].d_arg->d_tok.d_type == SynTree::R_ExpList ); + for( int j = 0; j < dopl[i].d_arg->d_children.size(); j++ ) + { + if( j != 0 ) + out << ", "; + emitExpression(ds, dopl[i].d_arg->d_children[j], out, 0 ); + } + } + out << ")"; + break; + case CodeModel::ArrayOp: + Q_ASSERT( dopl[i].d_arg->d_tok.d_type == SynTree::R_ExpList ); + for( int j = 0; j < dopl[i].d_arg->d_children.size(); j++ ) + { + out << "["; + emitExpression(ds, dopl[i].d_arg->d_children[j], out, 0 ); + out << "]"; // TODO: check for pointers? + } + break; + default: + break; + } + } + if( procCall && !dopl.isEmpty() && dopl.last().d_op != CodeModel::ProcedureOp ) + { + bool isCallWithoutParams = false; + if( const CodeModel::Element* e = dynamic_cast(dopl.last().d_sym) ) + { + isCallWithoutParams = ( e->d_kind == CodeModel::Element::StubProc ) || + ( e->d_type && e->d_type->d_kind == CodeModel::Type::ProcRef ); + }else if( const CodeModel::Procedure* p = dynamic_cast(dopl.last().d_sym) ) + isCallWithoutParams = true; + else if( const CodeModel::Type* t = dynamic_cast(dopl.last().d_sym) ) + isCallWithoutParams = t->d_kind == CodeModel::Type::ProcRef; + if( isCallWithoutParams ) + out << "()"; + } + + return printedSomething; +} + +void CppGen::emitTypeDecl(const CodeModel::Unit* ds, const CodeModel::Type* t, QTextStream& out, int level) +{ + Q_ASSERT( !t->d_name.isEmpty() ); + out << ws(level); + if( t->d_kind == CodeModel::Type::Record ) + { + emitRecDecl(ds,t,escape(t->d_name),out,level); + out << ";" << endl; + }else + { + out << "typedef "; + emitType( ds, t, out, level ); + out << " " << escape(t->d_name) << ";" << endl; + } +} + +void CppGen::emitVarDecl(const CodeModel::Unit* ds, const CodeModel::Element* v, QTextStream& out, int level) +{ + // auch Unknowns kommen hier an Q_ASSERT( v->d_kind == CodeModel::Element::Variable ); + out << ws(level); + if( v->d_type && v->d_type->d_kind == CodeModel::Type::ProcRef ) + { + emitProcType(ds,v->d_type,escape(v->d_name),out,level); + out << ";" << endl; + }else + { + emitType( ds, v->d_type, out, level ); + out << " " << escape(v->d_name) << ";" << endl; + } +} + +void CppGen::emitStubProcDecl(const CodeModel::Unit* ds, const CodeModel::Element* v, QTextStream& out, int level) +{ + Q_ASSERT( v->d_kind == CodeModel::Element::StubProc ); + + out << ws(level); + + if( v->d_type == 0 ) + out << "static void "; + else + { + emitType(ds,v->d_type,out,level); + out << " "; + } + out << escape(v->d_name); + emitProcParams( ds, v->d_vals, out ); + out << ";" << endl; +} + +void CppGen::emitRecDecl(const CodeModel::Unit* ds, const CodeModel::Type* r, const QByteArray& name, + QTextStream& out, int level) +{ + Q_ASSERT( r->d_kind == CodeModel::Type::Record ); + out << "struct " << name << ( name.isEmpty() ? "" : " " ); + out << ": public "; + if( r->d_type ) + emitType(ds,r->d_type,out,level); + else + out << "_Root"; + + out << " "; + + out << "{" << endl; + foreach( const CodeModel::Element* v, r->d_vals ) + { + emitVarDecl(ds,v,out,level+1); + } + out << ws(level) << "}"; +} + +void CppGen::emitProcDecl(const CodeModel::Procedure* p, QTextStream& out, int level) +{ + out << ws(level); + if( p->d_type == 0 ) + out << "static void "; + else + { + emitType(p,p->d_type,out,level); + out << " "; + } + out << escape(p->d_name); + emitProcParams(p, p->d_vals, out ); + out << ";" << endl; +} + +void CppGen::emitProcParams( const CodeModel::Unit* ds, const QList& params, QTextStream& out) +{ + out << "("; + for( int i = 0; i < params.size(); i++ ) + { + if( i != 0 ) + out << ", "; + const CodeModel::Element* par = params[i]; + const CodeModel::Type* t = derefed(par->d_type); + if( t && t->d_kind == CodeModel::Type::Array ) + { + if( par->d_var ) + out << "_VarArray<"; + else + out << "_ValArray<"; + emitType(ds, t->d_type, out, 0 ); + out << ">"; + }else + { + const bool isSimple = isSimpleType( par->d_type ); + if( !par->d_var && !isSimple ) + out << "const "; + emitType(ds,par->d_type,out,0); + if( par->d_var || !isSimple ) + out << "&"; + } + out << " " << escape(par->d_name); + } + out << ")"; +} + +void CppGen::emitProcBody(const CodeModel::Procedure* p, QTextStream& out) +{ + const CodeModel::Module* m = p->getModule(); + Q_ASSERT( m != 0 ); + + emitComment(p->d_def,out,0); + + if( p->d_type == 0 ) + out << "void "; + else + { + // Return kann nur ein Qualident sein + Q_ASSERT( p->d_type->d_kind == CodeModel::Type::TypeRef && p->d_type->d_st != 0 ); + // Hier einfach den Modulnamen vorstellen, falls d_type nicht bereits ein zweiteiliger Qualident ist + const CodeModel::Type* der = p->d_type->deref(); + if( p->d_type->d_st->d_children.size() == 1 && der && !der->isBasicType() ) + out << escape(p->d_scope->d_name) << "::"; + emitType(p,p->d_type,out,0); + out << " "; + } + out << escape(m->d_name) << "::" << escape(p->d_name); + emitProcParams(p, p->d_vals, out ); + out << endl << "{" << endl; + emitDecls(p,out,1); + + if( !p->d_body.isEmpty() ) + { + out << ws(1) << "// BEGIN" << endl; + out << ws(1) << escape(m->d_name) << "* _this = _inst();" << endl; + } + emitStatementSeq(p,p->d_body,out,1); + if( !p->d_body.isEmpty() ) + out << ws(1) << "// END" << endl; + + out << "}" << endl << endl; +} + +void CppGen::emitDecls(const CodeModel::Unit* ds, QTextStream& out, int l) +{ + QList consts = ds->getConsts(); + if( !consts.isEmpty() ) + out << ws(l) << "// CONST" << endl; + foreach( const CodeModel::Element* c, consts ) + { + emitComment( c->d_def, out,l ); + out << ws(l) << "static const "; + const CodeModel::Type* type = d_mdl->typeOfExpression(ds,c->d_st); + if( type == 0 ) + type = d_mdl->typeOfExpression(ds,c->d_st); + emitType( ds, type, out, l ); + out << " " << escape(c->d_name) << " = "; + emitExpression( ds, c->d_st, out, l ); + out << ";" << endl; + } + if( !consts.isEmpty() ) + out << endl; + + QList namedTypes = ds->getNamedTypes(); + if( !namedTypes.isEmpty() ) + out << ws(l) << "// TYPE" << endl; + bool hasForwards = false; + foreach( const CodeModel::Type* t, namedTypes ) + { + // forward declarations because of typedef pointer to record + if( t->d_kind == CodeModel::Type::Record ) + { + out << ws(l) << "struct " << escape(t->d_name) << ";" << endl; + hasForwards = true; + } + } + if( hasForwards ) + out << endl; + + foreach( const CodeModel::Type* t, namedTypes ) + { + emitComment( t->d_def, out,l ); + emitTypeDecl(ds,t,out,l); + } + if( !namedTypes.isEmpty() ) + out << endl; + + QList vars = ds->getVars(); + if( !vars.isEmpty() ) + out << ws(l) << "// VAR" << endl; + foreach( const CodeModel::Element* v, vars ) + { + emitComment( v->d_def, out,l ); + emitVarDecl(ds,v,out,l); + } + QList uknw = ds->getUnknowns(); + foreach( const CodeModel::Element* v, uknw ) + { + emitVarDecl(ds,v,out,l); + } + if( !vars.isEmpty() ) + out << endl; + + foreach( const CodeModel::Element* v, ds->getStubProcs() ) + { + emitStubProcDecl(ds,v,out,l); + } +} + +void CppGen::emitStatementSeq(const CodeModel::Unit* ds, const QList& seq, QTextStream& out, int level) +{ + int count = 0; + foreach( const SynTree* s, seq ) + { + if( s->d_tok.d_type == SynTree::R_statement ) + { + Q_ASSERT( s->d_children.size() <= 1 ); + if( s->d_children.isEmpty() ) + continue; + else + s = s->d_children.first(); + } + emitComment(s,out,level); + count++; + switch( s->d_tok.d_type ) + { + case SynTree::R_assignmentOrProcedureCall: + Q_ASSERT( false ); // wurde bereits vorher korrigiert + break; + case SynTree::R_assignment: + out << ws(level); + emitDesig(ds, s->d_children.first(), false, out, level); + out << " = "; + emitExpression(ds,s->d_children.last(), out, level ); + out << ";" << endl; + break; + case SynTree::R_ProcedureCall: + out << ws(level); + emitDesig(ds, s->d_children.first(), true, out, level); + out << ";" << endl; + break; + case SynTree::R_IfStatement: + emitIfStatement(ds, s, out, level ); + break; +// case SynTree::R_CaseStatement: +// out << ws(level) << "; // case statement" << endl; // TODO +// break; + case SynTree::R_WhileStatement: + emitWhileStatement(ds,s,out,level); + break; + case SynTree::R_RepeatStatement: + emitRepeatStatement(ds,s,out,level); + break; +// case SynTree::R_ForStatement: +// out << ws(level) << "; // for statement" << endl; // TODO +// break; + case SynTree::R_ReturnStatement: + out << ws(level) << "return "; + Q_ASSERT( s->d_children.size() == 2 && s->d_children.last()->d_tok.d_type == SynTree::R_expression ); + emitExpression(ds, s->d_children.last(), out, level ); + out << ";" << endl; + break; + default: + out << ws(level) << "; // unknown statement " << SynTree::rToStr(s->d_tok.d_type) << endl; + d_mdl->getErrs()->warning( Errors::Generator, s, + tr("'%1' not yet supported").arg(SynTree::rToStr(s->d_tok.d_type)) ); + break; + } + } + if( count == 0 ) + { + out << ws(level) << "; // empty statement" << endl; + } +} + +bool CppGen::emitPredefProc(const CodeModel::Unit* ds, const CodeModel::DesigOpList& dopl, QTextStream& out, int level) +{ + Q_ASSERT( !dopl.isEmpty() ); + const CodeModel::Element* pp = dopl.first().d_sym->to(); + Q_ASSERT( pp != 0 && pp->isPredefProc() ); + if( dopl.size() != 2 || dopl.last().d_op != CodeModel::ProcedureOp ) + { + d_errs->error( Errors::Semantics, dopl.first().d_arg, tr("invalid call of built-in procedure '%1'"). + arg(dopl.first().d_sym->d_name.data()) ); + return false; + } + Q_ASSERT( dopl.last().d_arg->d_tok.d_type == SynTree::R_ExpList ); + QList args = dopl.last().d_arg->d_children; + + switch( pp->d_kind ) + { + case CodeModel::Element::NEW: + { + const SynTree* desig = ( args.isEmpty() ? 0 : CodeModel::flatten(args.first(), SynTree::R_designator ) ); + if( args.size() != 1 || desig == 0 ) + { + d_errs->error( Errors::Semantics, dopl.last().d_arg, tr("invalid arguments of 'NEW()'") ); + return false; + } + CodeModel::DesigOpList arg = d_mdl->derefDesignator( ds, desig ); + const CodeModel::Element* id = 0; + const CodeModel::Type* ptr = 0; + const CodeModel::Type* rec = 0; + if( arg.isEmpty() || arg.last().d_op != CodeModel::IdentOp || + ( id = arg.last().d_sym->to() ) == 0 || + ( ptr = derefed( id->d_type ) ) == 0 || ptr->d_kind != CodeModel::Type::Pointer || + ( rec = derefed( ptr->d_type ) ) == 0 || rec->d_kind != CodeModel::Type::Record ) + { + d_mdl->getErrs()->error( Errors::Semantics, dopl.last().d_arg, tr("'NEW()' expects a POINTER to RECORD") ); + return false; + } + emitDesig(ds, desig, false, out, level ); + out << " = new "; + emitScopedName( ds, rec, out ); + out << "()"; +// if( rec->d_scope != ds ) +// out << escape(rec->d_scope->d_name) << "::"; +// out << escape(rec->d_name) << "()"; + return true; + } + break; + case CodeModel::Element::INC: + if( args.size() == 1 ) + { + emitExpression( ds, args.first(), out , level ); + out << "++"; + return true; + }else if( args.size() == 2 ) + { + emitExpression( ds, args.first(), out , level ); + out << " += "; + emitExpression( ds, args.last(), out , level ); + return true; + } + d_mdl->getErrs()->error( Errors::Semantics, pp->d_def, tr("'INC()' with invalid arguments") ); + break; + case CodeModel::Element::DEC: + if( args.size() == 1 ) + { + emitExpression( ds, args.first(), out , level ); + out << "--"; + return true; + }else if( args.size() == 2 ) + { + emitExpression( ds, args.first(), out , level ); + out << " -= "; + emitExpression( ds, args.last(), out , level ); + return true; + } + d_mdl->getErrs()->error( Errors::Semantics, dopl.last().d_arg, tr("'DEC()' with invalid arguments") ); + break; + case CodeModel::Element::ORD: + if( args.size() == 1 ) + { + const CodeModel::Type* t = d_mdl->typeOfExpression(ds, args.first() ); + if( t == d_mdl->getGlobalScope().d_setType ) + { + out << "( "; + emitExpression(ds, args.first(), out, level ); + out << " ).to_ulong()"; + return true; + }else + { + out << "int( "; + emitExpression(ds, args.first(), out, level ); + out << " )"; + return true; + } + } + d_mdl->getErrs()->error( Errors::Semantics, dopl.last().d_arg, tr("'ORD()' with invalid arguments") ); + break; + case CodeModel::Element::CHR: + if( args.size() == 1 ) + { + out << "char( "; + emitExpression(ds, args.first(), out, level ); + out << " )"; + return true; + } + d_mdl->getErrs()->error( Errors::Semantics, dopl.last().d_arg, tr("'CHR()' with invalid arguments") ); + break; + case CodeModel::Element::ODD: + if( args.size() == 1 ) + { + emitExpression(ds, args.first(), out, level ); + out << " % 2 == 1"; + return true; + } + d_mdl->getErrs()->error( Errors::Semantics, dopl.last().d_arg, tr("'ODD()' with invalid arguments") ); + break; + default: + d_mdl->getErrs()->warning( Errors::Generator, dopl.last().d_arg, + tr("built-in '%1()' not yet supported").arg(pp->d_name.data()) ); + break; + } + return false; +} + +static inline bool ifNeedsBlock( SynTree* stats ) +{ + Q_ASSERT( stats->d_tok.d_type == SynTree::R_StatementSequence ); + if( stats->d_children.isEmpty() ) + return false; + SynTree* stat = stats->d_children.first(); + Q_ASSERT( stat->d_tok.d_type == SynTree::R_statement ); + if( stat->d_children.isEmpty() ) + return false; + if( stat->d_children.first()->d_tok.d_type == SynTree::R_IfStatement ) + return true; + else + return stats->d_children.size() > 1; +} + +void CppGen::emitIfStatement(const CodeModel::Unit* ds, const SynTree* st, QTextStream& out, int level) +{ + Q_ASSERT( st != 0 && st->d_tok.d_type == SynTree::R_IfStatement && st->d_children.size() >= 4 && + st->d_children[1]->d_tok.d_type == SynTree::R_expression && + st->d_children[3]->d_tok.d_type == SynTree::R_StatementSequence ); + + out << ws(level) << "if( "; + emitExpression(ds,st->d_children[1],out,level); + out << " )" << endl; + SynTree* stats = st->d_children[3]; + bool block = ifNeedsBlock(stats); + if( block ) + out << ws(level) << "{" << endl; + emitStatementSeq(ds, stats->d_children, out, level + 1); + out << ws(level); + if( block ) + out << "}"; + for( int i = 4; i < st->d_children.size(); i++ ) + { + if( st->d_children[i]->d_tok.d_type == SynTree::R_ElsifStatement ) + { + SynTree* elif = st->d_children[i]; + Q_ASSERT( elif->d_children.size() == 4 && elif->d_children[1]->d_tok.d_type == SynTree::R_expression && + elif->d_children[3]->d_tok.d_type == SynTree::R_StatementSequence ); + out << "else if( "; + emitExpression(ds,elif->d_children[1],out,level); + out << " )" << endl; + SynTree* stats = elif->d_children[3]; + block = ifNeedsBlock(stats); + if( block ) + out << ws(level) << "{" << endl; + emitStatementSeq(ds, stats->d_children, out, level + 1); + out << ws(level); + if( block ) + out << "}"; + }else if( st->d_children[i]->d_tok.d_type == SynTree::R_ElseStatement ) + { + SynTree* els = st->d_children[i]; + Q_ASSERT( els->d_children.size() == 2 && els->d_children[1]->d_tok.d_type == SynTree::R_StatementSequence ); + out << "else" << endl; + SynTree* stats = els->d_children[1]; + block = ifNeedsBlock(stats); + if( block ) + out << ws(level) << "{" << endl; + emitStatementSeq(ds, stats->d_children, out, level + 1); + if( block ) + out << ws(level) << "}"; + } + } + out << endl; +} + +void CppGen::emitWhileStatement(const CodeModel::Unit* ds, const SynTree* st, QTextStream& out, int level) +{ + Q_ASSERT( st != 0 && st->d_tok.d_type == SynTree::R_WhileStatement && st->d_children.size() >= 4 && + st->d_children[1]->d_tok.d_type == SynTree::R_expression && + st->d_children[3]->d_tok.d_type == SynTree::R_StatementSequence ); + + out << ws(level) << "while( "; + emitExpression(ds,st->d_children[1],out,level); + out << " )" << endl; + SynTree* stats = st->d_children[3]; + if( stats->d_children.size() > 1 ) + out << ws(level) << "{" << endl; + emitStatementSeq(ds, stats->d_children, out, level + 1); + out << ws(level); + if( stats->d_children.size() > 1 ) + out << "}"; + out << endl; + if( CodeModel::findFirstChild( st, SynTree::R_ElsifStatement ) != 0 ) + d_errs->warning(Errors::Generator, st, tr("ELSIF statement in WHILE statement not supported") ); +} + +void CppGen::emitRepeatStatement(const CodeModel::Unit* ds, const SynTree* st, QTextStream& out, int level) +{ + Q_ASSERT( st != 0 && st->d_tok.d_type == SynTree::R_RepeatStatement && st->d_children.size() == 4 && + st->d_children[1]->d_tok.d_type == SynTree::R_StatementSequence && + st->d_children[3]->d_tok.d_type == SynTree::R_expression ); + + out << ws(level) << "do " << endl; + out << ws(level) << "{" << endl; + SynTree* stats = st->d_children[1]; + emitStatementSeq(ds, stats->d_children, out, level + 1); + out << ws(level) << "} while( !( "; + emitExpression(ds,st->d_children[3],out,level); + out << " ) );" << endl; +} + +void CppGen::emitSet(const CodeModel::Unit* ds, const SynTree* st, QTextStream& out, int level) +{ + Q_ASSERT( st->d_tok.d_type == SynTree::R_set && st->d_children.size() >= 2 ); + out << "( _Set() "; + + for( int i = 1; i < st->d_children.size() - 2; i++ ) + { + SynTree* el = st->d_children[i]; + Q_ASSERT( el->d_tok.d_type == SynTree::R_element && !el->d_children.isEmpty() ); + out << "+ "; + if( el->d_children.size() == 1 ) + { + out << "("; + emitExpression(ds, el->d_children.first(), out, level ); + out << ") "; + }else + { + out << "_Set( "; + emitExpression(ds, el->d_children.first(), out, level ); + out << ", "; + emitExpression(ds, el->d_children.last(), out, level ); + out << ") "; + } + } + out << ")"; +} + +void CppGen::emitScopedName(const CodeModel::Unit* ds, const CodeModel::NamedThing* nt, QTextStream& out) +{ + if( nt->d_scope != ds ) + out << escape(nt->d_scope->d_name) << "::"; + out << escape(nt->d_name); +} + +void CppGen::emitComment(const SynTree* st, QTextStream& out, int level ) +{ + if( st == 0 ) + return; + + while( d_nextCmt < d_cmts.size() && d_cmts[d_nextCmt].d_lineNr <= st->d_tok.d_lineNr ) + { + const QByteArray str = d_cmts[d_nextCmt++].d_val; + out << ws(level) << "/* " << str.mid(2,str.size()-4) << " */" << endl; + } +} + +bool CppGen::isSimpleType(const CodeModel::Type* orig) +{ + const CodeModel::Type* b = derefed(orig); + if( b ) + { + if( b->isBasicType() && b->d_kind != CodeModel::Type::STRING && b->d_kind != CodeModel::Type::SET ) + return true; + else if( b->d_kind == CodeModel::Type::Array ) + return false; + else if( b->d_kind == CodeModel::Type::Record ) + return false; + else if( b->d_kind == CodeModel::Type::Pointer ) + return true; + else if( b->d_kind == CodeModel::Type::ProcRef ) + return true; + else + return true; + } + return true; +} + +QByteArray CppGen::escape(const QByteArray& id) +{ + if( isCppKeyword(id) ) + return id + "_"; + else + return id; +} + +void CppGen::emitTerm(const CodeModel::Unit* ds,const SynTree* st, QTextStream& out, int level ) +{ + Q_ASSERT( st != 0 && st->d_tok.d_type == SynTree::R_term && !st->d_children.isEmpty() ); + int i = 0; + emitFactor(ds,st->d_children[i++],out,level); + while( i < st->d_children.size() ) + { + SynTree* op = CodeModel::flatten( st->d_children[i++] ); + switch( op->d_tok.d_type ) + { + case Tok_Star: + out << " * "; + break; + case Tok_Slash: + case Tok_DIV: + out << " / "; + break; + case Tok_MOD: + out << " % "; + break; + case Tok_Amp: + out << " && "; + break; + default: + out << " ?? "; + break; + } + Q_ASSERT( i < st->d_children.size() ); + emitFactor(ds,st->d_children[i++],out,level); + } +} + +void CppGen::emitSimpleExpression(const CodeModel::Unit* ds,const SynTree* st, QTextStream& out, int level) +{ + Q_ASSERT( st != 0 && st->d_tok.d_type == SynTree::R_SimpleExpression && !st->d_children.isEmpty() ); + int i = 0; + if( st->d_children[i]->d_tok.d_type == Tok_Plus || st->d_children[i]->d_tok.d_type == Tok_Minus ) + { + out << tokenTypeString(st->d_children[i]->d_tok.d_type); + i++; + } + emitTerm(ds,st->d_children[i++],out,level); + while( i < st->d_children.size() ) + { + SynTree* op = CodeModel::flatten( st->d_children[i++] ); + switch( op->d_tok.d_type ) + { + case Tok_Plus: + out << " + "; + break; + case Tok_Minus: + out << " - "; + break; + case Tok_OR: + out << " || "; + break; + default: + out << " ?? "; + break; + } + Q_ASSERT( i < st->d_children.size() ); + emitTerm(ds,st->d_children[i++],out,level); + } +} + +void CppGen::emitExpression(const CodeModel::Unit* ds,const SynTree* st, QTextStream& out, int level) +{ + Q_ASSERT( st != 0 && !st->d_children.isEmpty() ); + + SynTree* op = 0; + + if( st->d_children.size() > 1 ) + op = CodeModel::flatten( st->d_children[1] ); + + if( op && op->d_tok.d_type == Tok_IS ) + { + Q_ASSERT( st->d_children.size() == 3 ); + SynTree* q = d_mdl->flatten(st->d_children[2],SynTree::R_qualident); + out << "dynamic_cast<" << quali(q) << ">("; + emitSimpleExpression(ds,st->d_children.first(),out,level); + out << ") != 0 "; + }else if( op && op->d_tok.d_type == Tok_IN ) + { + Q_ASSERT( st->d_children.size() == 3 ); + emitSimpleExpression(ds,st->d_children.last(),out,level); + out << ".contains( "; + emitSimpleExpression(ds,st->d_children.first(),out,level); + out << " )"; + }else + { + emitSimpleExpression(ds,st->d_children.first(),out,level); + if( st->d_children.size() > 1 ) + { + switch( op->d_tok.d_type ) + { + case Tok_Eq: + out << " == "; + break; + case Tok_Hash: + out << " != "; + break; + case Tok_Lt: + out << " < "; + break; + case Tok_Leq: + out << " <= "; + break; + case Tok_Gt: + out << " > "; + break; + case Tok_Geq: + out << " >= "; + break; + case Tok_IN: + out << " IN "; + Q_ASSERT( false ); // wird oben gelöst + break; + case Tok_IS: + out << " IS "; + Q_ASSERT( false ); // wird oben gelöst + break; + default: + out << " ?? "; + break; + } + emitSimpleExpression(ds,st->d_children.last(),out,level); + } + } +} + diff --git a/ObCppGen.h b/ObCppGen.h new file mode 100644 index 0000000..308e637 --- /dev/null +++ b/ObCppGen.h @@ -0,0 +1,81 @@ +#ifndef OBCPPGEN_H +#define OBCPPGEN_H + +/* +* Copyright 2019 Rochus Keller +* +* This file is part of the Oberon parser/code model library. +* +* The following is the license that applies to this copy of the +* library. For a license to use the library under conditions +* other than those described here, please email to me@rochus-keller.ch. +* +* GNU General Public License Usage +* This file may be used under the terms of the GNU General Public +* License (GPL) versions 2.0 or 3.0 as published by the Free Software +* Foundation and appearing in the file LICENSE.GPL included in +* the packaging of this file. Please review the following information +* to ensure GNU General Public Licensing requirements will be met: +* http://www.fsf.org/licensing/licenses/info/GPLv2.html and +* http://www.gnu.org/copyleft/gpl.html. +*/ + +#include +#include +#include + +namespace Ob +{ + // Automatically translate Lola-2 compiler to C++. + + class CppGen : public QObject + { + public: + CppGen(CodeModel*); + void setGenStubs(bool on) { d_genStubs = on; } + bool emitModules(const QString& outdir, const QString& ns = QString(), const QString& mod = QString() ); + bool emitObnUtils( const QString& outdir, const QString& ns = QString(), const QString& mod = QString() ); + protected: + void emitModule(const CodeModel::Module*); + void emitHeader(const CodeModel::Module*, QTextStream& out, int level); + void emitBody(const CodeModel::Module*, QTextStream& out); + QString includeFileName( const QString& modName ); + QString ws(int level); + QString basicType( const CodeModel::Type* ); + void emitType(const CodeModel::Unit*,const CodeModel::Type* , QTextStream&, int level); + void emitProcType(const CodeModel::Unit*, const CodeModel::Type*, const QByteArray& name , QTextStream&, int level ); + void emitExpression( const CodeModel::Unit*, const SynTree*, QTextStream&, int level ); + void emitSimpleExpression(const CodeModel::Unit* ds,const SynTree* st, QTextStream& out, int level); + void emitTerm(const CodeModel::Unit* ds,const SynTree* st, QTextStream& out, int level ); + void emitFactor(const CodeModel::Unit* ds,const SynTree* st, QTextStream& out, int level ); + bool emitDesig(const CodeModel::Unit*,const SynTree* st, bool procCall, QTextStream&, int level); + void emitTypeDecl(const CodeModel::Unit*,const CodeModel::Type* , QTextStream&, int level); + void emitVarDecl(const CodeModel::Unit*, const CodeModel::Element* st, QTextStream&, int level); + void emitStubProcDecl(const CodeModel::Unit*, const CodeModel::Element* st, QTextStream&, int level); + void emitRecDecl(const CodeModel::Unit*,const CodeModel::Type*, const QByteArray& name, QTextStream&, int level); + void emitProcDecl( const CodeModel::Procedure*, QTextStream&, int level); + void emitProcParams(const CodeModel::Unit*, const QList&, QTextStream&); + void emitProcBody(const CodeModel::Procedure*p, QTextStream&out); + void emitDecls(const CodeModel::Unit*, QTextStream&, int level); + void emitStatementSeq( const CodeModel::Unit*, const QList& seq, QTextStream&, int level ); + bool emitPredefProc( const CodeModel::Unit*, const CodeModel::DesigOpList&, QTextStream&, int level ); + void emitIfStatement(const CodeModel::Unit* ds, const SynTree*,QTextStream& out, int level); + void emitWhileStatement(const CodeModel::Unit* ds, const SynTree*,QTextStream& out, int level); + void emitRepeatStatement(const CodeModel::Unit* ds, const SynTree*,QTextStream& out, int level); + void emitSet( const CodeModel::Unit*, const SynTree*,QTextStream& out, int level ); + void emitScopedName( const CodeModel::Unit*, const CodeModel::NamedThing*, QTextStream& out ); + void emitComment(const SynTree*, QTextStream& out, int level); + static bool isSimpleType( const CodeModel::Type* ); + static QByteArray escape( const QByteArray& ); + private: + CodeModel* d_mdl; + Errors* d_errs; + QString d_ns, d_outdir, d_mod; + QList d_cmts; + quint16 d_nextCmt; + quint16 d_nameNr; + bool d_genStubs; + }; +} + +#endif // OBCPPGEN_H diff --git a/OberonViewer.cpp b/OberonViewer.cpp index 02cbd26..4dd8e75 100644 --- a/OberonViewer.cpp +++ b/OberonViewer.cpp @@ -18,6 +18,7 @@ */ #include "NamedThingsMdl.h" +#include "ObCppGen.h" #include "OberonViewer.h" #include "ObnHighlighter.h" #include @@ -37,6 +38,8 @@ #include #include #include +#include +#include using namespace Ob; Q_DECLARE_METATYPE(Ob::CodeModel::Module*) @@ -365,6 +368,7 @@ OberonViewer::OberonViewer(QWidget *parent) : QMainWindow(parent),d_pushBackLock new QShortcut(tr("F3"),this,SLOT(onFindAgain()) ); new QShortcut(tr("F2"),this,SLOT(onGotoDefinition()) ); new QShortcut(tr("CTRL+O"),this,SLOT(onOpen()) ); + new QShortcut(tr("CTRL+T"),this,SLOT(onTranslate()) ); #if QT_VERSION > 0x050000 s_oldHandler = qInstallMessageHandler(messageHander); @@ -383,6 +387,7 @@ OberonViewer::OberonViewer(QWidget *parent) : QMainWindow(parent),d_pushBackLock logMessage(tr("CTRL+L to go to a specific line in the source code file") ); logMessage(tr("CTRL+F to find a string in the current file") ); logMessage(tr("CTRL+G or F3 to find another match in the current file") ); + logMessage(tr("CTRL+T to translate the Oberon files to C++") ); logMessage(tr("ALT+LEFT to move backwards in the navigation history") ); logMessage(tr("ALT+RIGHT to move forward in the navigation history") ); logMessage(tr("ESC to close Message Log") ); @@ -706,3 +711,48 @@ void OberonViewer::onOpen() showFiles(files); } +void OberonViewer::onTranslate() +{ + QDialog dlg(this); + dlg.setWindowTitle(tr("Translate to C++")); + QVBoxLayout* vbox = new QVBoxLayout(&dlg); + QFormLayout* form = new QFormLayout(); + QLabel* intro = new QLabel(&dlg); + intro->setText(tr("Convert all loaded Oberon files to C++. Note that currently only the\n" + "subset of Oberon-07 used by the Lola-2 compiler is supported\n" + "and that no garbage collector code is currently generated.\n" + "Check for errors or warnings in the log. For stubs only headers are generated.\n\n" + "WARNING: existing C++ files are overwritten without warning!") ); + vbox->addWidget(intro); + vbox->addLayout(form); + QSettings s; + QLineEdit* package = new QLineEdit(&dlg); + QLineEdit* ns = new QLineEdit(&dlg); + QLineEdit* path = new QLineEdit(&dlg); + package->setText(s.value("PackageName",tr("Lolac")).toString()); + ns->setText(s.value("Namespace",tr("Ll")).toString()); + path->setText(s.value("GenerateTo",QDir::currentPath()).toString()); + form->addRow(tr("Package name:"), package ); + form->addRow(tr("Namespace:"), ns ); + form->addRow(tr("Generate to:"), path ); + QDialogButtonBox* bb = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dlg ); + vbox->addWidget(bb); + connect(bb, SIGNAL(accepted()), &dlg, SLOT(accept())); + connect(bb, SIGNAL(rejected()), &dlg, SLOT(reject())); + if( dlg.exec() != QDialog::Accepted ) + return; + QString p = package->text().simplified(); + p.replace(' ',""); + s.setValue( "PackageName",p ); + QString n = ns->text().simplified(); + n.replace(' ',""); + s.setValue( "Namespace",n ); + s.setValue("GenerateTo", path->text() ); + + d_msgLog->clear(); + qDebug() << "generating C++ files..."; + Ob::CppGen g(d_mdl); + g.emitModules(path->text(),n,p); + qDebug() << "finished"; +} + diff --git a/OberonViewer.h b/OberonViewer.h index edc46a0..694d6c4 100644 --- a/OberonViewer.h +++ b/OberonViewer.h @@ -65,6 +65,7 @@ namespace Ob void onFindAgain(); void onGotoDefinition(); void onOpen(); + void onTranslate(); private: class Viewer; diff --git a/ObnCppMain.cpp b/ObnCppMain.cpp new file mode 100644 index 0000000..a863247 --- /dev/null +++ b/ObnCppMain.cpp @@ -0,0 +1,160 @@ +/* +* Copyright 2019 Rochus Keller +* +* This file is part of the Oberon to C++ (OBNCPP) transpiler. +* +* The following is the license that applies to this copy of the +* application. For a license to use the application under conditions +* other than those described here, please email to me@rochus-keller.ch. +* +* GNU General Public License Usage +* This file may be used under the terms of the GNU General Public +* License (GPL) versions 2.0 or 3.0 as published by the Free Software +* Foundation and appearing in the file LICENSE.GPL included in +* the packaging of this file. Please review the following information +* to ensure GNU General Public Licensing requirements will be met: +* http://www.fsf.org/licensing/licenses/info/GPLv2.html and +* http://www.gnu.org/copyleft/gpl.html. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "ObCodeModel.h" +#include "ObCppGen.h" +#include "ObErrors.h" + +static QStringList collectFiles( const QDir& dir ) +{ + QStringList res; + QStringList files = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name ); + + foreach( const QString& f, files ) + res += collectFiles( QDir( dir.absoluteFilePath(f) ) ); + + files = dir.entryList( QStringList() << QString("*.Mod") + << QString("*.mod"), + QDir::Files, QDir::Name ); + foreach( const QString& f, files ) + { + res.append( dir.absoluteFilePath(f) ); + } + return res; +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + a.setOrganizationName("Rochus Keller"); + a.setOrganizationDomain("https://github.com/rochus-keller/Oberon"); + a.setApplicationName("OBNCPP"); + a.setApplicationVersion("2019-04-09"); + + QTextStream out(stdout); + out << "OBNCPP version: " << a.applicationVersion() << + " author: me@rochus-keller.ch license: GPL" << endl; + + QStringList dirOrFilePaths; + QString outPath; + bool dump = false; + QString ns; + QString mod; + bool syntImports = true; + const QStringList args = QCoreApplication::arguments(); + for( int i = 1; i < args.size(); i++ ) // arg 0 enthaelt Anwendungspfad + { + if( args[i] == "-h" || args.size() == 1 ) + { + out << "usage: OBNCPP [options] sources" << endl; + out << " reads Oberon sources (files or directories) and translates them to corresponding C++ code." << endl; + out << "options:" << endl; + out << " -dsmi don't synthesize missing imports" << endl; + out << " -dst dump syntax trees to files" << endl; + out << " -o=path path where to save generated files (default like first source)" << endl; + out << " -ns=name namespace for the generated files (default empty)" << endl; + out << " -mod=name directory of the generated files (default empty)" << endl; + out << " -h display this information" << endl; + return 0; + }else if( args[i] == "-dsmi" ) + syntImports = false; + else if( args[i] == "-dst" ) + dump = true; + else if( args[i].startsWith("-o=") ) + outPath = args[i].mid(3); + else if( args[i].startsWith("-ns=") ) + ns = args[i].mid(4); + else if( args[i].startsWith("-mod=") ) + mod = args[i].mid(5); + else if( !args[ i ].startsWith( '-' ) ) + { + dirOrFilePaths += args[ i ]; + }else + { + qCritical() << "error: invalid command line option " << args[i] << endl; + return -1; + } + } + if( dirOrFilePaths.isEmpty() ) + { + qWarning() << "no file or directory to process; quitting (use -h option for help)" << endl; + return -1; + } + + QStringList files; + foreach( const QString& path, dirOrFilePaths ) + { + QFileInfo info(path); + if( outPath.isEmpty() ) + outPath = info.isDir() ? info.absoluteFilePath() : info.absolutePath(); + if( info.isDir() ) + files += collectFiles( info.absoluteFilePath() ); + else + files << path; + } + + qDebug() << "processing" << files.size() << "files..."; + Ob::CodeModel m; + m.getErrs()->setRecord(false); + m.setSynthesize(syntImports); + m.parseFiles(files); + + if( dump ) + { + qDebug() << "dumping module syntax trees to files..."; + foreach( const Ob::CodeModel::Module* o, m.getGlobalScope().d_mods ) + { + if( o->d_def == 0 ) + continue; + QFileInfo fi(o->d_def->d_tok.d_sourcePath ); + QDir dir = fi.dir(); + if( !mod.isEmpty() ) + { + dir.mkpath(mod); + dir.cd(mod); + } + QFile out( dir.absoluteFilePath( fi.baseName() + ".txt") ); + if( !out.open(QIODevice::WriteOnly) ) + { + qDebug() << "error: cannot open dump file for writing" << out.fileName(); + continue; + } + QTextStream ts(&out); + Ob::CodeModel::dump(ts,o->d_def); + } + } + + qDebug() << "generating files..."; + Ob::CppGen g(&m); + g.emitModules(outPath,ns,mod); + + if( m.getErrs()->getErrCount() == 0 && m.getErrs()->getWrnCount() == 0 ) + qDebug() << "successfully completed"; + else + qDebug() << "completed with" << m.getErrs()->getErrCount() << "errors and" << + m.getErrs()->getWrnCount() << "warnings"; + return 0; +} diff --git a/ObnViewer.pro b/ObnViewer.pro index 204b9d7..d52f77d 100644 --- a/ObnViewer.pro +++ b/ObnViewer.pro @@ -30,7 +30,9 @@ SOURCES += \ ObnViewerMain.cpp \ OberonViewer.cpp \ ObnHighlighter.cpp \ - NamedThingsMdl.cpp + NamedThingsMdl.cpp \ + CppKeywordDetector.cpp \ + ObCppGen.cpp include( Oberon.pri ) @@ -38,12 +40,15 @@ CONFIG(debug, debug|release) { DEFINES += _DEBUG } +!win32{ QMAKE_CXXFLAGS += -Wno-reorder -Wno-unused-parameter -Wno-unused-function -Wno-unused-variable +} HEADERS += \ OberonViewer.h \ ObnHighlighter.h \ - NamedThingsMdl.h + NamedThingsMdl.h \ + ObCppGen.h diff --git a/ObnViewerMain.cpp b/ObnViewerMain.cpp index fefc8ad..d58c9fa 100644 --- a/ObnViewerMain.cpp +++ b/ObnViewerMain.cpp @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) a.setOrganizationName("me@rochus-keller.ch"); a.setOrganizationDomain("github.com/rochus-keller/Oberon"); a.setApplicationName("OberonViewer"); - a.setApplicationVersion("0.5.3"); + a.setApplicationVersion("0.6"); a.setStyle("Fusion"); QStringList dirOrFilePaths; diff --git a/README.md b/README.md index 1dc0e25..9282f30 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -## Welcome to the Oberon-07 C++ parser and code model/browser +## Welcome to the Oberon-07 C++ parser and code model/browser/generator -This is an Oberon-07 parser and code model written in C++ and Qt. See http://www.projectoberon.net/wirth/Oberon/Oberon07.Report.pdf for more information about the language. The syntax was modified for Coco/R compatibility using https://github.com/rochus-keller/EbnfStudio. +This is an Oberon-07 parser, code model and generator written in C++ and Qt. See http://www.projectoberon.net/wirth/Oberon/Oberon07.Report.pdf for more information about the language. The syntax was modified for Coco/R compatibility using https://github.com/rochus-keller/EbnfStudio. The goal of this project is to build tools to better understand the Lola-2 compiler and to automatically translate it to maintainable C++ with minimal dependencies to other C++ libraries and with no dependencies to the Oberon System. The C++ based Lola-2 compiler will be integrated in https://github.com/rochus-keller/LolaCreator. @@ -10,6 +10,18 @@ The goal of this project is to build tools to better understand the Lola-2 compi - Syntax and semantics validation, error reporting - Infers and synthesizes missing modules +### C++ Code generator features + +- Generates C++03 compatible code with no other dependencies than the standard libraries +- Generates stub headers for the synthesized (missing) modules to ease implementing the missing parts +- Arrays including strings are implemented by C++ template classes +- Modules are dynamically created to maintain the correct initialization dependencies +- Comments of the original files are also translated +- Oberon idents conflicting with C++ keywords are postfixed by underscore +- The generated code is well readable and maintainable +- Currently only the subset of Oberon-07 used by the Lola-2 compiler is supported; see https://github.com/rochus-keller/Lolac for an example of the generated code +- There is no garbage collector code generated yet, but an arena based collector can easily be implemented outside of the generator by customizing the _Root class; future versions will generate a regular mark & sweep collector. + ### Code browser features - Syntax highlighting @@ -21,6 +33,7 @@ The goal of this project is to build tools to better understand the Lola-2 compi ![OberonViewer Screenshot](http://software.rochus-keller.info/oberonviewer_screenshot_1.png) + ### Binary version Here is a binary version of OberonViewer for Windows: http://software.rochus-keller.info/OberonViewer_win32.zip diff --git a/_Global-Template.h b/_Global-Template.h new file mode 100644 index 0000000..f8984cc --- /dev/null +++ b/_Global-Template.h @@ -0,0 +1,159 @@ +#ifndef OBN_GLOBAL_H +#define OBN_GLOBAL_H + +/* +* Copyright 2019 Rochus Keller +* +* This file is part of the Oberon parser/code model library. +* +* The following is the license that applies to this copy of the +* library. For a license to use the library under conditions +* other than those described here, please email to me@rochus-keller.ch. +* +* GNU General Public License Usage +* This file may be used under the terms of the GNU General Public +* License (GPL) versions 2.0 or 3.0 as published by the Free Software +* Foundation and appearing in the file LICENSE.GPL included in +* the packaging of this file. Please review the following information +* to ensure GNU General Public Licensing requirements will be met: +* http://www.fsf.org/licensing/licenses/info/GPLv2.html and +* http://www.gnu.org/copyleft/gpl.html. +*/ + +#include +#include +#include +#include +#include + +// NOTE: change file name, include guards and namespace if needed. +// Adding arena based garbage collection can easily be accomplished by +// extending the _Root constructor to add each instance to a global list +// which then can be deleted at suited times (e.g. between calls to Lolac). + +namespace Obn +{ + typedef unsigned char uint8_t; + + struct _Root + { + _Root() {} + virtual ~_Root() {} + + template + T _to() + { + T res = dynamic_cast(this); + if( res == 0 ) + { + std::cout << typeName() << "_to" << typeid(T).name() << std::endl; + throw "type guard error"; + } + // assert( res != 0 ); + return res; + } + virtual const char* typeName() const { return "_Root"; } + }; + + template + class _ValArray + { + public: + _ValArray():d_val(0),d_len(0) {} + template + _ValArray( const T2& rhs ):d_len(rhs.size()),d_val(rhs.data()) { } + const T& operator[]( int n ) const { assert( n < size() ); return d_val[n]; } + int size() const { return ( d_len < 0 ? -d_len : d_len ); } + const T* data() const { return d_val; } + protected: + const T* d_val; + int d_len; + }; + + template<> + class _ValArray + { + public: + _ValArray():d_val(0),d_len(0) {} + template + _ValArray(const T2& rhs):d_len(rhs.size()),d_val(rhs.data()) { } + _ValArray(const char* str):d_val(str),d_len( ::strlen( str ) + 1 ){} + _ValArray(int ch):d_val(0),d_len( -2 ){ assert( ch < 255 ); d_str[0] = ch; } // wegen Konstanten der form 0x09 + _ValArray(char ch):d_val(0),d_len( -2 ){ d_str[0] = ch; } + char operator[]( int n ) const { assert( n < size() ); if( d_len < 0 ) return d_str[n]; else return d_val[n]; } + int size() const { return ( d_len < 0 ? -d_len : d_len ); } + const char* data() const { return ( d_len < 0 ? d_str : d_val ); } + protected: + union { const char* d_val; char d_str[sizeof(int)]; }; + int d_len; + }; + + template + class _VarArray + { + public: + _VarArray():d_val(0),d_len(0) {} + template + _VarArray( T2& rhs ):d_len(rhs.size()),d_val(rhs.data()) { } + T& operator[](int n) { assert( n < d_len ); return d_val[n]; } + const T& operator[]( int n ) const { assert( n < d_len ); return d_val[n]; } + int size() const { return d_len; } + const T* data() const { return d_val; } + protected: + T* d_val; + int d_len; + }; + + template + class _FxArray + { + public: + _FxArray() { clear(); } + + void operator=( const char* rhs ) + { assert( ::strlen(rhs) < LEN ); clear(); ::strcpy(d_arr,rhs); } + void operator=( char c ) + { assert( LEN >= 2 ); clear(); d_arr[0] = c; } + template + void operator=( const T2& rhs ) + { assert( rhs.size() <= size() ); clear(); for( int i = 0; i < rhs.size(); i++ ) d_arr[i] = rhs[i]; } + + template + bool operator==( const T2& rhs ) const { if( size() != rhs.size() ) return false; else + for( int i = 0; i < rhs.size(); i++ ) if( d_arr[i] != rhs[i] ) return false; return true; } + template + bool operator==( const _FxArray& rhs ) const { return ::strcmp( d_arr, rhs.d_arr ) == 0; } + bool operator==( const _ValArray& rhs ) const { return ::strcmp( d_arr, rhs.data() ) == 0; } + bool operator==( const _VarArray& rhs ) const { return ::strcmp( d_arr, rhs.data() ) == 0; } + bool operator==( const char* rhs ) const { return ::strcmp( d_arr, rhs ) == 0; } + template + bool operator!=( const T2& rhs ) const { return !( *this == rhs ); } + + T& operator[](int n) { assert( n < LEN ); return d_arr[n]; } + const T& operator[](int n) const { assert( n < LEN ); return d_arr[n]; } + + int size() const { return LEN; } + const T* data() const { return d_arr; } + T* data() { return d_arr; } + + void clear() { for( int i = 0; i < LEN; i++ ) d_arr[i] = T(); } + protected: + T d_arr[LEN]; + }; + + class _Set : public std::bitset<32> + { + public: + _Set() {} + _Set( int from, int to ) + { + for( int i = from; i <= to; i++ ) + set(i); + } + _Set& operator+( int rhs ) { set(rhs); return *this; } + _Set& operator+( const _Set& rhs ) { *this |= rhs; return *this; } + bool contains(int rhs) const { return test(rhs); } + }; +} + +#endif // OBN_GLOBAL_H