maple
This commit is contained in:
parent
5bce4b2705
commit
62641b5da6
@ -354,8 +354,8 @@ required in NodeJS scripts.
|
||||
|
||||
### Connecting to S3
|
||||
|
||||
The `AWS` module includes a function `S3` that performs the connection. Access
|
||||
keys for an IAM user[^9] must be used:
|
||||
The `aws-sdk` module includes a function `S3` that performs the connection.
|
||||
Access keys for an IAM user[^9] must be used:
|
||||
|
||||
```js
|
||||
/* credentials */
|
||||
|
@ -21,7 +21,7 @@ data from opaque spreadsheets and parse the data from Mathematica.
|
||||
|
||||
:::note
|
||||
|
||||
This demo was last tested in 2023 August 21 in Mathematica 13.2.1.
|
||||
This demo was last tested by SheetJS users on 2023 August 21 in Mathematica 13.
|
||||
|
||||
:::
|
||||
|
||||
|
@ -21,7 +21,7 @@ spreadsheets into simple XLSX files for MATLAB.
|
||||
|
||||
:::note
|
||||
|
||||
This demo was last tested in 2023 September 12 in MATLAB R2023a.
|
||||
This demo was last tested by SheetJS users on 2023 September 12 in MATLAB R2023a.
|
||||
|
||||
:::
|
||||
|
||||
|
209
docz/docs/03-demos/32-extensions/12-maple.md
Normal file
209
docz/docs/03-demos/32-extensions/12-maple.md
Normal file
@ -0,0 +1,209 @@
|
||||
---
|
||||
title: Modern Spreadsheets in Maple
|
||||
sidebar_label: Maple
|
||||
pagination_prev: demos/cloud/index
|
||||
pagination_next: demos/bigdata/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[Maple](https://www.maplesoft.com/products/Maple/) is a numeric computing
|
||||
platform. It offers a robust C-based extension system.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses SheetJS to pull data from a spreadsheet for further analysis
|
||||
within Maple. We'll create a Maple native extension that loads the
|
||||
[Duktape](/docs/demos/engines/duktape) JavaScript engine and uses the SheetJS
|
||||
library to read data from spreadsheets and converts to a Maple-friendly format.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
ofile[(workbook\nXLSB file)]
|
||||
nfile[(clean file\nXLSX)]
|
||||
data[[Maple\nTable]]
|
||||
ofile --> |Maple Extension\nSheetJS + Duktape| nfile
|
||||
nfile --> |ExcelTools\nImport|data
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
This demo was last tested by SheetJS users on 2023 October 3 in Maple 2023.
|
||||
|
||||
:::
|
||||
|
||||
:::info pass
|
||||
|
||||
Maple has limited support for processing spreadsheets through the `ExcelTools`
|
||||
package[^1]. At the time of writing, it lacked support for XLSB, NUMBERS, and
|
||||
other common spreadsheet formats.
|
||||
|
||||
SheetJS libraries help fill the gap by normalizing spreadsheets to a form that
|
||||
Maple can understand.
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The current recommendation involves a native plugin that reads arbitrary files
|
||||
and generates clean XLSX files that Maple can import.
|
||||
|
||||
The extension function ultimately pairs the SheetJS `read`[^2] and `write`[^3]
|
||||
methods to read data from the old file and write a new file:
|
||||
|
||||
```js
|
||||
var wb = XLSX.read(original_file_data, {type: "buffer"});
|
||||
var new_file_data = XLSX.write(wb, {type: "array", bookType: "xlsx"});
|
||||
```
|
||||
|
||||
The extension function will receive a file name and perform the following steps:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
ofile{{File\nName}}
|
||||
subgraph JS Operations
|
||||
ojbuf[(Buffer\nFile Bytes)]
|
||||
wb(((SheetJS\nWorkbook)))
|
||||
njbuf[(Buffer\nXLSX bytes)]
|
||||
end
|
||||
obuf[(File\nbytes)]
|
||||
nbuf[(New file\nbytes)]
|
||||
nfile[(XLSX\nFile)]
|
||||
ofile --> |C\nRead File| obuf
|
||||
obuf --> |Duktape\nBuffer Ops| ojbuf
|
||||
ojbuf --> |SheetJS\n`read`| wb
|
||||
wb --> |SheetJS\n`write`| njbuf
|
||||
njbuf --> |Duktape\nBuffer Ops| nbuf
|
||||
nbuf --> |C\nWrite File| nfile
|
||||
```
|
||||
|
||||
### C Extensions
|
||||
|
||||
Maple C extensions are shared libraries or DLLs that use special Maple methods
|
||||
for parsing arguments and returning values.
|
||||
|
||||
To simplify the flow, the new function will take one argument (the original file
|
||||
name) and return one value (the new file name).
|
||||
|
||||
The official documentation has a comprehensive list[^4] of methods. For this
|
||||
demo, the following methods are used:
|
||||
|
||||
- `MapleNumArgs` and `IsMapleString` are used in argument validation. The demo
|
||||
function will raise a Maple exception if no file name is specified.
|
||||
|
||||
- `MapleRaiseError` and `MapleRaiseError2` programmatically raise errors.
|
||||
|
||||
- `MapleToString` and `ToMapleString` convert between Maple and C strings.
|
||||
|
||||
### Duktape JS Engine
|
||||
|
||||
This demo uses the [Duktape JavaScript engine](/docs/demos/engines/duktape). The
|
||||
SheetJS + Duktape demo covers engine integration details in more detail.
|
||||
|
||||
The [SheetJS Standalone scripts](/docs/getting-started/installation/standalone)
|
||||
can be loaded in Duktape by reading the source from the filesystem.
|
||||
|
||||
## Complete Demo
|
||||
|
||||
:::info pass
|
||||
|
||||
This demo was tested in Windows x64. The path names and build commands will
|
||||
differ in other platforms and operating systems.
|
||||
|
||||
:::
|
||||
|
||||
The [`sheetjs-maple.c`](pathname:///maple/sheetjs-maple.c) extension exports the
|
||||
`SheetToXLSX` Maple method. It takes a file name argument, parses the specified
|
||||
file, exports data to `sheetjsw.xlsx` and returns the string `"sheetjsw.xlsx"`.
|
||||
|
||||
This can be chained with `Import` from `ExcelTools`:
|
||||
|
||||
```maple
|
||||
with(ExcelTools);
|
||||
Import(SheetToXLSX("pres.numbers"))
|
||||
```
|
||||
|
||||
0) Ensure "Windows Subsystem for Linux" (WSL) and Visual Studio are installed.
|
||||
|
||||
1) Open a new "x64 Native Tools Command Prompt" window and create a project
|
||||
folder `c:\sheetjs-maple`:
|
||||
|
||||
```powershell
|
||||
cd c:\
|
||||
mkdir sheetjs-maple
|
||||
cd sheetjs-maple
|
||||
```
|
||||
|
||||
2) Copy the headers and `lib` files from the Maple folder to the project folder.
|
||||
For example, using Maple 2023 on Windows x64:
|
||||
|
||||
```powershell
|
||||
copy "C:\Program Files\Maple 2023\extern\include\"*.h .
|
||||
copy "c:\Program Files\Maple 2023\bin.x86_64_WINDOWS"\*.lib .
|
||||
```
|
||||
|
||||
3) Run `bash` to enter WSL
|
||||
|
||||
4) Within WSL, install Duktape:
|
||||
|
||||
```bash
|
||||
curl -LO https://duktape.org/duktape-2.7.0.tar.xz
|
||||
tar -xJf duktape-2.7.0.tar.xz
|
||||
mv duktape-2.7.0/src/*.{c,h} .
|
||||
```
|
||||
|
||||
5) Still within WSL, download SheetJS scripts and the test file.
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/shim.min.js
|
||||
curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js
|
||||
curl -LO https://sheetjs.com/pres.numbers`}
|
||||
</CodeBlock>
|
||||
|
||||
6) Still within WSL, download the extension C code
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/maple/sheetjs-maple.c
|
||||
```
|
||||
|
||||
7) Exit WSL by running `exit`. The window will return to the command prompt.
|
||||
|
||||
8) Build the extension DLL:
|
||||
|
||||
```powershell
|
||||
cl -Gz sheetjs-maple.c duktape.c /EHsc -link -dll -out:sheetjs-maple.dll maplec.lib
|
||||
```
|
||||
|
||||
9) Close and re-open Maple, then create a new Maple Worksheet or Document
|
||||
|
||||
10) Run the following command in Maple to change the working directory:
|
||||
|
||||
```maple
|
||||
currentdir("c:\\sheetjs-maple");
|
||||
```
|
||||
|
||||
11) Load the `SheetToXLSX` method from the extension:
|
||||
|
||||
```maple
|
||||
with(ExternalCalling):
|
||||
dll:=ExternalLibraryName("sheetjs-maple"):
|
||||
SheetToXLSX:=DefineExternal("SheetToXLSX",dll):
|
||||
```
|
||||
|
||||
12) Read the `pres.numbers` test file:
|
||||
|
||||
```maple
|
||||
with(ExcelTools);
|
||||
Import(SheetToXLSX("pres.numbers"))
|
||||
```
|
||||
|
||||
The result will show the data from `pres.numbers`
|
||||
|
||||
![Maple Screenshot](pathname:///maple/maple.png)
|
||||
|
||||
[^1]: See ["ExcelTools"](https://www.maplesoft.com/support/help/Maple/view.aspx?path=ExcelTools) in the Maple documentation.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^4]: See ["C OpenMaple and ExternalCalling Application Program Interface (API)"](https://www.maplesoft.com/support/help/maple/view.aspx?path=OpenMaple%2FC%2FAPI) in the Maple documentation.
|
BIN
docz/static/maple/maple.png
Normal file
BIN
docz/static/maple/maple.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
115
docz/static/maple/sheetjs-maple.c
Normal file
115
docz/static/maple/sheetjs-maple.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "maplec.h"
|
||||
#include "duktape.h"
|
||||
|
||||
/* --- EXPORT_DECL macro from official example --- */
|
||||
|
||||
#if !defined(EXPORT_DECL)
|
||||
#ifdef _MSC_VER
|
||||
#define EXPORT_DECL __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORT_DECL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* --- the SheetJS + Duktape demo cover these machinations --- */
|
||||
|
||||
#define FAIL_LOAD { \
|
||||
duk_push_undefined(ctx); \
|
||||
perror("Error in load_file"); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
static char *read_file(const char *filename, size_t *sz) {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if(!f) return NULL;
|
||||
long fsize; { fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); }
|
||||
char *buf = (char *)malloc(fsize * sizeof(char));
|
||||
*sz = fread((void *) buf, 1, fsize, f);
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static duk_int_t eval_file(duk_context *ctx, const char *filename) {
|
||||
size_t len; char *buf = read_file(filename, &len);
|
||||
if(!buf) FAIL_LOAD
|
||||
|
||||
duk_push_lstring(ctx, (const char *)buf, (duk_size_t)len);
|
||||
duk_int_t retval = duk_peval(ctx);
|
||||
duk_pop(ctx);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static duk_int_t load_file(duk_context *ctx, const char *filename, const char *var) {
|
||||
size_t len; char *buf = read_file(filename, &len);
|
||||
if(!buf) FAIL_LOAD
|
||||
|
||||
duk_push_external_buffer(ctx);
|
||||
duk_config_buffer(ctx, -1, buf, len);
|
||||
duk_put_global_string(ctx, var);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static duk_int_t save_file(duk_context *ctx, const char *filename, const char *var) {
|
||||
duk_get_global_string(ctx, var);
|
||||
duk_size_t sz;
|
||||
char *buf = (char *)duk_get_buffer_data(ctx, -1, &sz);
|
||||
|
||||
if(!buf) return 1;
|
||||
FILE *f = fopen(filename, "wb"); fwrite(buf, 1, sz, f); fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FAIL_DUK(cmd) { \
|
||||
const char *errmsg = duk_safe_to_string(ctx, -1); \
|
||||
duk_destroy_heap(ctx); \
|
||||
MapleRaiseError2(kv, "error in %1 : %2", ToMapleString(kv, cmd), ToMapleString(kv, errmsg)); \
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
#define DOIT(cmd) duk_eval_string_noresult(ctx, cmd);
|
||||
|
||||
/* SheetToXLSX function */
|
||||
EXPORT_DECL ALGEB M_DECL SheetToXLSX( MKernelVector kv, ALGEB *args ) {
|
||||
duk_int_t res = 0;
|
||||
|
||||
/* get filename */
|
||||
if(MapleNumArgs(kv, (ALGEB)args) != 1) {
|
||||
MapleRaiseError(kv, "must specify a filename");
|
||||
return NULL;
|
||||
}
|
||||
if(!IsMapleString(kv, args[1])) {
|
||||
MapleRaiseError(kv, "filename must be a string");
|
||||
return NULL;
|
||||
}
|
||||
const char *filename = MapleToString(kv, args[1]);
|
||||
|
||||
/* initialize duktape */
|
||||
duk_context *ctx = duk_create_heap_default();
|
||||
/* duktape does not expose a standard "global" by default */
|
||||
DOIT("var global = (function(){ return this; }).call(null);");
|
||||
|
||||
/* load SheetJS library */
|
||||
res = eval_file(ctx, "shim.min.js");
|
||||
if(res != 0) FAIL_DUK("shim load")
|
||||
res = eval_file(ctx, "xlsx.full.min.js");
|
||||
if(res != 0) FAIL_DUK("library load")
|
||||
|
||||
/* read file */
|
||||
res = load_file(ctx, filename, "buf");
|
||||
if(res != 0) FAIL_DUK("file load")
|
||||
printf("Loaded file %s\n", filename);
|
||||
|
||||
/* parse workbook and write to XLSX */
|
||||
DOIT("wb = XLSX.read(buf.slice(0, buf.length), {type:'buffer'});");
|
||||
DOIT("newbuf = (XLSX.write(wb, {type:'array', bookType:'xlsx'}));");\
|
||||
|
||||
/* write file */
|
||||
res = save_file(ctx, "sheetjsw.xlsx", "newbuf");\
|
||||
if(res != 0) FAIL_DUK("save sheetjsw.xlsx")
|
||||
|
||||
/* return filename */
|
||||
return ToMapleString(kv, "sheetjsw.xlsx");
|
||||
}
|
Loading…
Reference in New Issue
Block a user