diff --git a/examples/ecl_qt/.gitkeep b/examples/ecl_qt/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/examples/ecl_qt/Makefile b/examples/ecl_qt/Makefile new file mode 100644 index 000000000..fca1ceeb4 --- /dev/null +++ b/examples/ecl_qt/Makefile @@ -0,0 +1,13 @@ +all:lisp-envi.a hello-lisp-system--all-systems.fasb + +#lisp environment. +lisp-envi.a: lisp-envi.asd lisp-envi.lisp build_static.lisp + ecl -load build_static.lisp + +#your lisp system. +hello-lisp-system--all-systems.fasb: hello-lisp-system.asd hello-lisp.lisp \ +build_fasl.lisp + ecl -load build_fasl.lisp + +clean: + -rm -f hello-lisp-system--all-systems.fasb lisp-envi.a diff --git a/examples/ecl_qt/README.md b/examples/ecl_qt/README.md new file mode 100644 index 000000000..d37c3622d --- /dev/null +++ b/examples/ecl_qt/README.md @@ -0,0 +1,32 @@ +This demo shows how to embed ECL into Qt5 and serve as kernel. This also discuss how to compile ECL with C++(14). You can extend on this demo to form a more complicate and productive project. + +# Preparation +Before you build the demo, make sure you have those dependencies installed: +1. ECL, of course. We recommend version 16.1.2. +2. g++/clang compiler with at least C++14 support. +3. make +4. Qt5.x with Qt Creator. +5. Quicklisp installed on your ECL. + +We use the external Lisp package :lparallel so you better download that package in advance using (ql:quickload :lparallel). + +# Build +## Build CL Library and FASB +Run `make` in current directory and you get two files, if successful. `lisp-envi.a` and `hello-lisp-system--all-systems.fasb`. +## Configure your Qt Project +cd to the directory `qt` and open that Qt project with your Qt Creator. Change the three paths I marked for you, if necessary. +1. `INCLUDEPATH`: The path that contains ecl/ecl.h. +In Linux it may be `/usr/include/`. +2. `LIBS`:The path that leads to the shared library of ECL. +In Linux, it may be `/usr/lib/libecl.so/`. +## Build Qt Project +Build your Qt Project. This will generate an executable file for you. +## Engage `fasb` file +After your Qt project is built, move the `hello-lisp-system--all-systems.fasb` file that generated in build step 1 into the directory containing the executable file. + +# Run +After you go through the steps above, go for the executable file and try that demo. + +Happy hacking with ECL! + +ntr(Lexicall) diff --git a/examples/ecl_qt/build_fasl.lisp b/examples/ecl_qt/build_fasl.lisp new file mode 100644 index 000000000..1a0ac253a --- /dev/null +++ b/examples/ecl_qt/build_fasl.lisp @@ -0,0 +1,8 @@ +;;(require 'asdf) +(push "./" asdf:*central-registry*) + +(asdf:make-build :hello-lisp-system + :type :fasl + :monolithic t + :move-here "./") +(quit) diff --git a/examples/ecl_qt/build_static.lisp b/examples/ecl_qt/build_static.lisp new file mode 100644 index 000000000..375b233ce --- /dev/null +++ b/examples/ecl_qt/build_static.lisp @@ -0,0 +1,8 @@ +;;(require 'asdf) +(push "./" asdf:*central-registry*) + +(asdf:make-build :lisp-envi + :type :static-library + :move-here "./") +(quit) + diff --git a/examples/ecl_qt/hello-lisp-system.asd b/examples/ecl_qt/hello-lisp-system.asd new file mode 100644 index 000000000..f190c1fce --- /dev/null +++ b/examples/ecl_qt/hello-lisp-system.asd @@ -0,0 +1,4 @@ +(defsystem :hello-lisp-system + :depends-on (:lparallel) + :components ((:file "hello-lisp"))) + diff --git a/examples/ecl_qt/hello-lisp.lisp b/examples/ecl_qt/hello-lisp.lisp new file mode 100644 index 000000000..012124425 --- /dev/null +++ b/examples/ecl_qt/hello-lisp.lisp @@ -0,0 +1,33 @@ + +(defpackage :hello-lisp + (:use :cl :lparallel)) + +(in-package :hello-lisp) ;;package name hello-lisp + + +(setf lparallel:*kernel* (lparallel:make-kernel 4)) + +(lparallel:defpun pfib (n) + (if (< n 2) + n + (plet ((a (pfib (- n 1))) + (b (pfib (- n 2)))) + (+ a b)))) + + +(defun qsort (seq pred) + (if (null seq) nil + (let* ((pivot (first seq)) + (left (remove-if-not (lambda (x) + (funcall pred x pivot)) + (cdr seq))) + (right (remove-if (lambda (x) + (funcall pred x pivot)) + (cdr seq)))) + (append (qsort left pred) + (list pivot) + (qsort right pred))))) + + +(defun say-hello () + "Bonjour, lisp!") diff --git a/examples/ecl_qt/lisp-envi.asd b/examples/ecl_qt/lisp-envi.asd new file mode 100644 index 000000000..2e92abb0e --- /dev/null +++ b/examples/ecl_qt/lisp-envi.asd @@ -0,0 +1,3 @@ +(defsystem :lisp-envi + :depends-on () + :components ((:file "lisp-envi"))) diff --git a/examples/ecl_qt/lisp-envi.lisp b/examples/ecl_qt/lisp-envi.lisp new file mode 100644 index 000000000..ba10d40f0 --- /dev/null +++ b/examples/ecl_qt/lisp-envi.lisp @@ -0,0 +1,3 @@ +(princ "Lisp Environment Booted.") + + diff --git a/examples/ecl_qt/qt/.gitkeep b/examples/ecl_qt/qt/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/examples/ecl_qt/qt/cl_bridge_utils.cpp b/examples/ecl_qt/qt/cl_bridge_utils.cpp new file mode 100644 index 000000000..49eca85fd --- /dev/null +++ b/examples/ecl_qt/qt/cl_bridge_utils.cpp @@ -0,0 +1,11 @@ +#include "cl_bridge_utils.hpp" + + +cl_object lispfy(string str){ + return c_string_to_object(str.data()); +} + +string __spc_expr(string first){ + return first; +} + diff --git a/examples/ecl_qt/qt/cl_bridge_utils.hpp b/examples/ecl_qt/qt/cl_bridge_utils.hpp new file mode 100644 index 000000000..eabca2fa5 --- /dev/null +++ b/examples/ecl_qt/qt/cl_bridge_utils.hpp @@ -0,0 +1,103 @@ +#ifndef CL_BRIDGE_UTILS_HPP +#define CL_BRIDGE_UTILS_HPP +#include +#include +#ifdef slots +#undef slots +#endif +#include + +using std::string; +using lisp_expr = std::string; +using std::cout; +using std::endl; +//extern string CL_MAIN_FASB ; +//extern string CL_MAIN_PACKAGE_NAME ; + +cl_object lispfy(string str); /* convert a std::string to cl_object */ + + + +/* add spaces among several strings. */ +string __spc_expr(string first); +template +string __spc_expr (string first, str ... next){ + return first+" "+__spc_expr(next...); +} + +/* encapsule expressions in parenthesis. */ +/* to create lisp expr. */ +template +lisp_expr par_expr(str... all){ + return "("+__spc_expr(all...)+")"; +} + +/* turn the sequence into a lisp list expr. */ +/* ex: par_list("hello", "lisp", "world"); + * -> '("hello" "lisp" "world") */ +template +lisp_expr par_list(str... all){ + return "'"+par_expr(all...); +} + +/* an enhanced version of cl_eval */ +template +cl_object cl_eval(str... all){ + std::cout<__obj=obj;} + cl_obj(const cl_object &obj){this->__obj=obj;} + + /* list index */ + inline cl_obj car(){return cl_obj(cl_car(this->__obj));} + inline cl_obj cdr(){return cl_obj(cl_cdr(this->__obj));} + inline cl_obj cadr(){return this->cdr().car();} + inline cl_obj caar(){return this->car().car();} + inline cl_obj cddr(){return this->cdr().cdr();} + + /* predicates */ + inline bool nullp(){return Null(this->__obj);} + inline bool atomp(){return ECL_ATOM(this->__obj);} + inline bool listp(){return ECL_LISTP(this->__obj);} + inline bool symbolp(){return ECL_SYMBOLP(this->__obj);} + + inline int to_int(){return ecl_to_int(this->__obj);} + inline char to_char(){return ecl_to_char(this->__obj);} + + /* turn the cl_object into string. */ + inline std::string to_std_string(){ + std::string val; + auto & str=this->__obj->string; + for(unsigned long i=0;i + inline void list_traverse(function fn){cl_list_traverse(this->__obj, fn);} + + inline cl_obj operator=(cl_object &&obj){return cl_obj(obj);} + +}; + + + +#endif // CL_BRIDGE_UTILS_HPP diff --git a/examples/ecl_qt/qt/ecl_qtdemo.pro b/examples/ecl_qt/qt/ecl_qtdemo.pro new file mode 100644 index 000000000..8e1692d9f --- /dev/null +++ b/examples/ecl_qt/qt/ecl_qtdemo.pro @@ -0,0 +1,35 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2016-08-10T18:00:40 +# +#------------------------------------------------- + +QT += core gui + +CONFIG+=c++14 +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = ecl_qtdemo +TEMPLATE = app + + +SOURCES += main.cpp\ + hybrid_main.cpp \ + cl_bridge_utils.cpp + +HEADERS += hybrid_main.h \ + cl_bridge_utils.hpp + +FORMS += hybrid_main.ui + +#The include path that contains ecl/ecl.h +INCLUDEPATH += /usr/local/include +#The ECL shared library directory. +LIBS += /usr/local/lib/libecl.dylib + +LIBS += $$_PRO_FILE_PWD_/../lisp-envi.a + + +RESOURCES += \ + resource.qrc + diff --git a/examples/ecl_qt/qt/hybrid_main.cpp b/examples/ecl_qt/qt/hybrid_main.cpp new file mode 100644 index 000000000..71c1b99c4 --- /dev/null +++ b/examples/ecl_qt/qt/hybrid_main.cpp @@ -0,0 +1,76 @@ +#include "hybrid_main.h" +#include "ui_hybrid_main.h" +#include +#include +#include "cl_bridge_utils.hpp" +using ss=std::stringstream; +using std::cout; +using std::endl; +hybrid_main::hybrid_main(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::hybrid_main) +{ + ui->setupUi(this); +} + + +hybrid_main::~hybrid_main() +{ + delete ui; +} + +/* int -> string */ +auto itos=[](auto in){ + ss s;s<>res; + return res; +}; + +/* when called, an alert dialog shows up */ +auto jump_out_alert_window=[](std::string str){ + QMessageBox::critical(0 , + "critical message" , QString::fromStdString(str), + QMessageBox::Ok | QMessageBox::Default , + QMessageBox::Cancel | QMessageBox::Escape , 0 ); + +}; + +/* concurrent fibonacci */ +void hybrid_main::on_pushButton_clicked() +{ + auto str=ui->edit->text().toStdString(); + if(str==""){ + jump_out_alert_window("You haven't input anything!"); + } else { + cl_obj rtv=cl_eval("pfib", str); + string strt=itos(rtv.to_int()); + ui->ans->setText(QString::fromStdString(strt)); + } +} + +/* quick sort. */ +void hybrid_main::on_pushButton_2_clicked() +{ + auto str=ui->input->text().toStdString(); + if(str=="") + { + jump_out_alert_window("You haven't input anything!"); + } else { + cout<output->setText(QString::fromStdString(lab)); + } +} + + + + +/* hello lisp */ +void hybrid_main::on_pushButton_3_clicked() +{ + string s=cl_obj(cl_eval("say-hello")).to_std_string(); + jump_out_alert_window(s); +} diff --git a/examples/ecl_qt/qt/hybrid_main.h b/examples/ecl_qt/qt/hybrid_main.h new file mode 100644 index 000000000..dcbdae4df --- /dev/null +++ b/examples/ecl_qt/qt/hybrid_main.h @@ -0,0 +1,32 @@ +#ifndef HYBRID_MAIN_H +#define HYBRID_MAIN_H + +#include + +namespace Ui { +class hybrid_main; +} + +class hybrid_main : public QMainWindow +{ + Q_OBJECT + +public: + explicit hybrid_main(QWidget *parent = 0); + ~hybrid_main(); + +private slots: + void on_pushButton_clicked(); + + void on_pushButton_2_clicked(); + + void on_pushButton_3_clicked(); + +private: + Ui::hybrid_main *ui; +}; + + + + +#endif // HYBRID_MAIN_H diff --git a/examples/ecl_qt/qt/hybrid_main.ui b/examples/ecl_qt/qt/hybrid_main.ui new file mode 100644 index 000000000..941dd4d7a --- /dev/null +++ b/examples/ecl_qt/qt/hybrid_main.ui @@ -0,0 +1,248 @@ + + + hybrid_main + + + + 0 + 0 + 641 + 430 + + + + hybrid_main + + + + + + 112 + 60 + 121 + 21 + + + + + + + + + + Input N here. + + + + + + 240 + 55 + 113 + 32 + + + + calculate! + + + + + + 30 + 60 + 71 + 16 + + + + Fibonacci: + + + + + + 160 + 100 + 241 + 16 + + + + Quick Sort List Processing Test + + + + + + 30 + 130 + 59 + 16 + + + + Input + + + + + + 30 + 170 + 59 + 16 + + + + Output + + + + + + 20 + 200 + 113 + 32 + + + + sort! + + + + + + 110 + 130 + 491 + 21 + + + + Input a sequence of number, seperate by space. + + + + + + 110 + 170 + 491 + 21 + + + + true + + + Sorted sequence output + + + + + + 30 + 80 + 601 + 16 + + + + Qt::Horizontal + + + + + + 270 + 240 + 61 + 91 + + + + border-image:url(:/pic/madeinlisp.png) + + + + + + + + + 370 + 300 + 191 + 41 + + + + (Core made in Lisp.) + + + + + + 160 + 10 + 301 + 16 + + + + Concurrent Compution Test (lparallel) + + + + + + 350 + 270 + 181 + 32 + + + + Hello, Lisp! + + + + + + 360 + 60 + 113 + 21 + + + + + + + true + + + Answer output + + + + + + 400 + 240 + 111 + 16 + + + + String Test + + + + + + + + diff --git a/examples/ecl_qt/qt/madeinlisp.png b/examples/ecl_qt/qt/madeinlisp.png new file mode 100644 index 000000000..520c4fbba Binary files /dev/null and b/examples/ecl_qt/qt/madeinlisp.png differ diff --git a/examples/ecl_qt/qt/main.cpp b/examples/ecl_qt/qt/main.cpp new file mode 100644 index 000000000..7c848db0a --- /dev/null +++ b/examples/ecl_qt/qt/main.cpp @@ -0,0 +1,43 @@ +#include "hybrid_main.h" +#include +#include "cl_bridge_utils.hpp" + +string CL_MAIN_FASB = "\"hello-lisp-system--all-systems.fasb\""; +string CL_MAIN_PACKAGE_NAME = "hello-lisp"; + +/* Initialization. + * This time we load the fasb file after + * the Lisp Environment is booted. + * */ +#define __cl_init_name init_lib_LISP_ENVI + +extern "C"{ + + extern void __cl_init_name(cl_object); + +} + +void init_cl_env(int argc, char * argv[]){ + /* Initialize CL environment */ + cl_boot(argc, argv); + ecl_init_module(NULL, __cl_init_name); + /* load fasb */ + cl_eval("load", CL_MAIN_FASB); + /* set context to current package */ + cl_eval("in-package", CL_MAIN_PACKAGE_NAME); + /* hook for shutting down cl env */ + atexit(cl_shutdown); +} + +#undef __cl_init_name + + + + +int main(int argc, char *argv[]){ + QApplication a(argc, argv); + hybrid_main w; + w.show(); + init_cl_env(argc, argv); /* init env */ + return a.exec(); +} diff --git a/examples/ecl_qt/qt/resource.qrc b/examples/ecl_qt/qt/resource.qrc new file mode 100644 index 000000000..79809ee68 --- /dev/null +++ b/examples/ecl_qt/qt/resource.qrc @@ -0,0 +1,5 @@ + + + madeinlisp.png + +