first commit

This commit is contained in:
Kizarm 2023-11-26 15:56:00 +01:00
parent 50d0a2c3c9
commit c8d704a06c
18 changed files with 871 additions and 0 deletions

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
# kdevelop
.kde*
*.kdev4
# other
*.zip
*.wasm
*.lst
*.map
*.o

33
Makefile Normal file
View file

@ -0,0 +1,33 @@
CC = clang
CX = clang++
CODE ?= wasm
MOBJS = calc.yy.o calc.tab.o calculator.o
ifeq ($(CODE),wasm)
include wasm.mk
else
include unix.mk
endif
OBJS = $(MOBJS) $(MAIN)
CFLAGS += -Wno-unused-function
all: $(PR)
%.o: %.cpp
$(CX) -std=c++14 -c $(CFLAGS) -fno-exceptions -fno-rtti $< -o $@
%.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
calc.tab.o: calc.tab.c
$(CX) -std=c++14 -c $(CFLAGS) -Wno-deprecated -Wno-writable-strings -fno-exceptions -fno-rtti $< -o $@
$(PR): $(OBJS) $(WALIB)
$(LD) $(LFLAGS) $(OBJS) -o $(PR) $(LDLIBS)
calc.yy.cpp: calc.l calc.tab.h
flex calc.l
calc.tab.c calc.tab.h: calc.y
bison -d calc.y
clean:
rm -f *.o *.yy.* *.tab.*
distclean: clean
rm -f $(PR)
.PHONY: all clean distclean

46
bin/index.html Normal file
View file

@ -0,0 +1,46 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CALC</title>
<style>
tr, td { border-collapse: collapse; }
table { width:100%; border-collapse: collapse; text-align: center; }
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
textarea.emscripten { font-family: monospace; font-size: 16px; width: 100%; overflow-x: scroll; white-space: pre; background: black; color: rgb(0,255,0);}
.frame1 { width: 96%; margin: 0; padding: 10px; background-color: #FFFFC0; border: 10px solid #F0C0F0; }
.canvas { width: 100%; height: 65vh; background-color: black; }
</style>
</head>
<body>
<script type="module" src="index.js"></script>
<div class="frame1"><canvas id="canvas" class="canvas"></canvas></div>
<div class="frame1"><input id="buttonTest" type="button" value="Submit"></div>
<div class="frame1">
<table><tr>
<td width="40%"><textarea class="emscripten" id="input" rows="10" spellcheck="false"></textarea></td>
<td><textarea readonly class="emscripten" id="stdout" rows="10" spellcheck="false"></textarea></td>
</tr></table>
</div>
<div class="frame1">
<h2>Kalkulátor s grafickým výstupem.</h2>
<p>Když jsem přepisoval z nudy syntax highlighter pro C++ z pythonu do C++, zjistil jsem, že regulární výrazy
jsou kupodivu v tom pythonu efektivnější. V tomto ohledu je STL knihovna asi dost naprd. Ale vzpomněl jsem si
na prastarý pár flex a bison, který umí nejen regulární výrazy, ale jde s tím parsovat dost jednoduše gramatika.
Mělo by to jít i v C++, ale příklady na webu byly dost zamotané a bylo nutné použít STL, kterou jsem pro tento
účel neměl k dispozici. Vyřešilo se to jednoduše - vygenerovaný C-čkový kód se přeloží jako C++, přičemž je
nutné povypínat něktetré warningy.
</p>
<p>Je to tedy jednoduchý kalkulátor, jde napsat výraz s normální notací (+-*/^), obsahující čísla (i desetinná),
který to normálně vyhodnotí. Postupně přibyly proměnné (jen písmenkové řetězce), které mohou mít i rozsah ve
kterém se pak výraz zobrazí jako funkce. Komentáře jsou ve složených závorkách. Vložené matematické funkce
jsou sin(), cos(), exp(), log() (přirozené).<b>Na konci výrazu musí být ENTER.</b>
</p>
<p>Jsou v tom chyby, celé je to vlasně hloupost, celé by to šlo napsat v javascriptu mnohem jednodušeji,
ale v podstatě to funguje a jde si podle toho udělat představu, jak daná funkce vypadá. Zdrojáky v licenci
MIT <a href="flex.zip">přikládám</a>. Pro kompilaci je použit jen clang a jím kompilovaná C-čková knihovna
<a href="https://sourceware.org/newlib/" target="_blank">newlib</a>.
</p>
</div>
</body>
</html>

126
bin/index.js Normal file
View file

@ -0,0 +1,126 @@
var gWASM; // globální proměnná + elementy stránky
const Outs = document.getElementById('stdout');
const Btnt = document.getElementById('buttonTest');
const Canvas = document.getElementById('canvas');
// async/await z příkladu na webu
window.onload = async function() {
Btnt.disabled = true;
Outs.value = 'Compiling ...¨\n'; // presets
// Build WebAssembly instance - WebAssembly.instantiateStreaming problem
const memory = new WebAssembly.Memory({ initial: 4 });
const importObject = {
env: { memory }, // nutné pro práci a pamětí
imports: { // importované funkce do wasm
printout : (ptr, len) => {
// pohled do paměti - ptr je vlastně číslo
const view = new Uint8Array (memory.buffer, ptr, len);
const utf8decoder = new TextDecoder();
Outs.value += utf8decoder.decode(view); // to String
},
memoryGrow : (len) => {
console.log ('Growing the memory by ' + len.toString() + '. 64K blocks');
memory.grow (len); // patrně to jde volat přímo z C/C++ kódu, ale tohle funguje
},
drawPoints : (px, py, len, pj, jl) => {
const xview = new Float32Array (memory.buffer, px, len);
const yview = new Float32Array (memory.buffer, py, len);
const view = new Uint8Array (memory.buffer, pj, jl);
const utf8decoder = new TextDecoder();
const obj = JSON.parse (utf8decoder.decode(view));
// console.log (obj);
if (!obj) obj = '{"name":"nothing"}';
polyLine (xview, yview, len, obj);
},
},
};
const response = await fetch('./module.wasm');
const bytes = await response.arrayBuffer();
const module = await WebAssembly.instantiate(bytes, importObject);
gWASM = {
asm : module.instance.exports,
mem : memory,
};
gWASM.asm.init(memory.buffer.byteLength);
Btnt.onclick = () => {
Outs.value = '';
const expression = document.getElementById('input').value;
// console.log(expression);
stringToModule (expression, gWASM.asm.compute);
};
Outs.value = 'Module compiled - [insert formula and] press button Submit\n';
Btnt.disabled = false;
// console.log (gWASM);
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
getFile ('test.txt');
Splash ();
};
function stringToModule (str, mfunc) {
const utf8EncodeText = new TextEncoder();
const bytes = utf8EncodeText.encode(str);
// alokovat pamet v modulu je nutne, aby bylo kam kopirovat
const cArrayPointer = gWASM.asm.cAlloc(bytes.length);
if (!cArrayPointer) return;
const cArray = new Uint8Array(gWASM.mem.buffer, cArrayPointer, bytes.length);
cArray.set(bytes); // naplnit dekodovanym stringem
mfunc (cArrayPointer, cArray.length);
}
function resizeCanvas () {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
canvas.width = width;
canvas.height = height;
// console.log (width, height);
gWASM.asm.resizeCanvas (width, height);
}
function drawLine (ctx, line, x, y) {
ctx.beginPath();
ctx.moveTo(line.b[0], line.b[1]);
ctx.lineTo(line.e[0], line.e[1]);
ctx.lineWidth = line.w;
ctx.strokeStyle = line.color;
if (line.lbl) {
//console.log (line.lbl);
ctx.fillStyle = line.color;
ctx.fillText (line.lbl, line.b[0] + x, line.b[1] - y);
}
ctx.stroke();
}
function polyLine (ax, ay, len, obj) {
// console.log (ax, ay);
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = '16px serif';
ctx.textAlign = 'left';
if (obj.name === "axes") {
drawLine (ctx, obj.x, 0, 0);
drawLine (ctx, obj.y, 0, 0);
const ndotsx = obj.xdots.length;
for (let n=0; n<ndotsx; n++) { drawLine (ctx, obj.xdots[n], 0, 5); }
const ndotsy = obj.ydots.length;
for (let n=0; n<ndotsy; n++) { drawLine (ctx, obj.ydots[n], 10, 0); }
}
ctx.beginPath();
ctx.moveTo(ax[0], ay[0]);
for (let n=1; n<len; n++) {
ctx.lineTo(ax[n], ay[n]);
}
ctx.lineWidth = 3;
ctx.strokeStyle = "#00ff00";
ctx.stroke();
}
async function getFile (name) {
const response = await fetch (name);
if (!response.ok) return;
const bytes = await response.arrayBuffer();
const array = new Uint8Array(bytes);
const decoder = new TextDecoder();
document.getElementById('input').value = decoder.decode(bytes);
}
function Splash () {
const content = Canvas.getContext('2d');
content.font = '96px serif';
content.fillStyle = '#00FF00';
content.textAlign = 'center';
content.fillText ('Graphic calculator', Canvas.width / 2, Canvas.height / 2);
}

7
bin/test.txt Normal file
View file

@ -0,0 +1,7 @@
{vlnový balík}
max=10
x=-max,max,1000
a=1/4
omega=2*pi
phi=omega/4
1.e-8 * sin (omega*x - phi) * exp (-(x*a)^2)

31
calc.l Normal file
View file

@ -0,0 +1,31 @@
%{
#include <stdlib.h>
#include "calc.tab.h"
#include "calculator.h"
%}
%option outfile="calc.yy.cpp" header-file="calc.yy.h"
%option case-sensitive
%option array
D [0-9]
E [Ee][+-]?{D}+
%%
pi { return MATHPI; }
sin { return FCESIN; }
cos { return FCECOS; }
exp { return FCEEXP; }
log { return FCELOG; }
[a-zA-Z]+ { yylval.svalue = strdup (yytext); return IDENT; }
{D}+ { yylval.svalue = strdup (yytext); return INTEGER; }
{D}*\.{D}+({E})? { yylval.svalue = strdup (yytext); return DOUBLE; }
{D}*\.{D}*({E})? { yylval.svalue = strdup (yytext); return DOUBLE; }
\{.*\}
[ \t]+ /* ignored */
\n { return ENDLINE; }
. { return yytext[0]; }
%%
void prevent_unused (void) {
(void) yyunput;
/* (void) input; */
}

68
calc.y Normal file
View file

@ -0,0 +1,68 @@
%{
#include <stdio.h>
#include "calculator.h"
int yylex (void);
int yyerror (char *);
%}
%language "C"
%union {
char * svalue;
double dvalue;
class Expression * evalue;
}
%token ENDLINE
%token DOUBLE
%token INTEGER
%token MATHPI
%token FCESIN
%token FCECOS
%token FCEEXP
%token FCELOG
%token IDENT
%token TEXT
%left '+' '-'
%left '*' '/'
%left '^'
%type <svalue> ENDLINE DOUBLE INTEGER IDENT line lines
%type <evalue> expr
%type <dvalue> constnt
%start input
%%
input: /* empty */
| lines
;
lines: line
| lines line
;
line: ENDLINE
| IDENT '=' expr ENDLINE { addVariable ($1, $3->eval()); free ($1); }
| IDENT '=' expr ',' expr ',' expr ENDLINE { addRange ($1, $3->eval(), $5->eval(), $7->eval()); free ($1); }
| expr ENDLINE { addExpression ($1); }
| error ENDLINE { printf ("what:%s\n", $$); }
;
constnt: DOUBLE { $$ = str_to_double ($1); free ($1); }
| INTEGER { $$ = str_to_long ($1); free ($1); }
| MATHPI { $$ = 3.14159265359; }
;
expr: constnt { $$ = new Constant ($1); }
| IDENT { $$ = new Variable ($1); free ($1); }
| '-' expr { $$ = new Unary ($2, UNARY_MINUS); }
| expr '+' expr { $$ = new Binary ($1, $3, BINARY_PLUS); }
| expr '-' expr { $$ = new Binary ($1, $3, BINARY_MINUS);}
| expr '*' expr { $$ = new Binary ($1, $3, BINARY_MULT); }
| expr '/' expr { $$ = new Binary ($1, $3, BINARY_DIV); }
| expr '^' expr { $$ = new Binary ($1, $3, BINARY_POW); }
| '(' expr ')' { $$ = $2; }
| FCESIN '(' expr ')' { $$ = new Unary ($3, UNARY_SIN); }
| FCECOS '(' expr ')' { $$ = new Unary ($3, UNARY_COS); }
| FCEEXP '(' expr ')' { $$ = new Unary ($3, UNARY_EXP); }
| FCELOG '(' expr ')' { $$ = new Unary ($3, UNARY_LOG); }
;
%%
int yyerror(char * err) {
printf("Error:\"%s\"\n", err);
return 0;
}

82
calculator.cpp Normal file
View file

@ -0,0 +1,82 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "calculator.h"
double str_to_double (const char * str) {
return strtod (str, NULL);
}
long str_to_long (const char * str) {
return strtol (str, NULL, 10);
}
static double u_minus (const double x) { return -x; }
const unary_function unary_function_table [UNARY_max] = {
u_minus,
sin, cos, exp, log,
};
static double b_plus (const double a, const double b) { return a + b; }
static double b_minus (const double a, const double b) { return a - b; }
static double b_multiply (const double a, const double b) { return a * b; }
static double b_divide (const double a, const double b) { return b !=0.0 ? a / b : 1.0; }
const binary_function binary_function_table [BINARY_max] = {
b_plus, b_minus, b_multiply, b_divide, pow,
};
static CommonData * main_data = nullptr;
void addVariable (const char * str, const double x) {
printf ("\"%s\"\t= %g\n", str, x);
if (main_data->list == nullptr) {
main_data->list = new VariableList (str, x);
return;
}
main_data->list->add(str, x);
}
void addRange (const char * str, const double x, const double e, const double s) {
printf ("\"%s\"\t= [%g:%g],%g\n", str, x, e, s);
if (main_data->list == nullptr) {
main_data->list = new VariableList (str, x, e, s);
return;
}
main_data->list->add(str, x, e, s);
}
static Range zero = {0.0, 0.0, 0.0};
Variable::Variable(const char * name) {
data = main_data->list->find (name);
if (!data) {
printf("variable \"%s\" not assigned, zero used\n", name);
data = & zero;
}
}
extern void emitData (double * x, double * y, const int len);
void addExpression (Expression * e) {
if (main_data->root) delete main_data->root;
main_data->root = e;
VariableList * vl = main_data->list->find_ranged();
if (!vl) {
printf("f() = %g\n", e->eval());
return;
}
Range * r = vl->get();
const double x0 = r->value, x1 = r->end, s = fabs (r->step);
int n = (x1 - x0) / s;
if (n <= 0) return;
double * vx = new double [n + 1];
double * vy = new double [n + 1];
// printf("n=%d\n", n);
for (int i=0; i<n+1; i++) {
const double x = x0 + (double) i * s;
r->value = x;
const double y = e->eval();
vx[i] = x; vy[i] = y;
}
emitData (vx, vy, n+1);
delete [] vx; delete [] vy;
}
void initData () {
main_data = new CommonData();
}
void finiData () {
delete main_data;
}

121
calculator.h Normal file
View file

@ -0,0 +1,121 @@
/* inclusion guard */
#ifndef __CALCULATOR_H__
#define __CALCULATOR_H__
#include <stdlib.h>
#include <string.h>
extern double str_to_double (const char * str);
extern long str_to_long (const char * str);
extern void addVariable (const char * str, const double x);
extern void addRange (const char * str, const double x, const double e, const double s);
extern void initData ();
extern void finiData ();
class Expression {
public:
virtual double eval () = 0;
virtual ~Expression () {}
};
class Constant : public Expression {
double data;
public:
explicit Constant (const double x) : data (x) {}
explicit Constant (const long x) : data (x) {}
double eval () override { /*printf("c=%g", data);*/ return data; }
virtual ~Constant() {}
};
extern void addExpression (Expression * e);
enum UNARY_FNCS {
UNARY_MINUS = 0, UNARY_SIN, UNARY_COS, UNARY_EXP, UNARY_LOG, UNARY_max
};
enum BINARY_FNCS {
BINARY_PLUS = 0, BINARY_MINUS, BINARY_MULT, BINARY_DIV, BINARY_POW, BINARY_max
};
typedef double (*unary_function) (const double);
typedef double (*binary_function) (const double, const double);
extern const unary_function unary_function_table [UNARY_max];
extern const binary_function binary_function_table [BINARY_max];
class Unary : public Expression {
Expression * m_expr;
unary_function m_f;
public:
explicit Unary (Expression * e, const UNARY_FNCS nf) : m_expr(e), m_f (unary_function_table[nf]) {}
double eval () override { /*printf("(*%p)(%p)", m_f, m_expr);*/ return (m_f (m_expr->eval())); }
virtual ~Unary () { delete m_expr; }
};
class Binary : public Expression {
Expression * m_l, * m_r;
binary_function m_f;
public:
explicit Binary (Expression * l, Expression * r, const BINARY_FNCS nf) : m_l(l), m_r(r), m_f(binary_function_table[nf]) {}
double eval () override { /*printf("{%p}[%p]{%p}", m_l, m_f, m_r);*/ return (m_f (m_l->eval(), m_r->eval())); }
virtual ~Binary() { delete m_l; delete m_r; }
};
struct Range {
double value, end, step;
};
class VariableList {
VariableList * next;
char * name;
Range data;
public:
explicit VariableList (const char * _name, const double _value) : next(nullptr) {
name = strdup (_name);
data.value = _value;
data.end = _value;
data.step = 0.0;
}
explicit VariableList (const char * _name, const double _value, const double _end, const double steps) : next(nullptr) {
name = strdup (_name);
if (_value > _end) {
data.value = _end;
data.end = _value;
} else {
data.value = _value;
data.end = _end;
}
if (steps != 0.0) data.step = (data.end - data.value) / steps;
else data.step = 1.0;
}
~VariableList () {
if (next) delete next;
free (name);
}
void add (const char * _name, const double _value) {
if (!next) next = new VariableList (_name, _value);
else next->add (_name, _value);
}
void add (const char * _name, const double _value, const double _end, const double steps) {
if (!next) next = new VariableList (_name, _value, _end, steps);
else next->add (_name, _value, _end, steps);
}
Range * find (const char * _name) {
if (!strcmp (name, _name)) return & data;
if (next) return next->find (_name);
return nullptr;
}
VariableList * find_ranged () {
if (data.step) return this;
if (next) return next->find_ranged();
return nullptr;
}
Range * get () { return & data; }
};
class Variable : public Expression {
Range * data;
public:
explicit Variable (const char * name);
double eval() override { return data->value; }
virtual ~Variable () {}
};
struct CommonData {
VariableList * list;
Expression * root;
explicit CommonData() : list(nullptr), root(nullptr) {}
~CommonData () {
if (list) delete list;
if (root) delete root;
}
};
#endif /* __CALCULATOR_H__ */

46
heap.c Normal file
View file

@ -0,0 +1,46 @@
#include "heap.h"
extern void IMPORT(memoryGrow) (const int block);
extern char __heap_base;
typedef __SIZE_TYPE__ size_t;
static const char * _HEAP_START = &__heap_base;
char * _HEAP_MAX = &__heap_base;
void * sbrk (unsigned long size) {
static const char * heap_ptr;
const char * old_heap_ptr;
static unsigned int init_sbrk = 0;
/* heap_ptr is initialized to HEAP_START */
if (init_sbrk == 0) {
heap_ptr = _HEAP_START;
init_sbrk = 1;
}
old_heap_ptr = heap_ptr;
/* Tohle je jen zkusmo, uvidíme, zatím se zdá, že to chodí.
* Těžko říct, co to udělá, když dojde paměť, ale pár MiB to zvládne.
*/
if ((heap_ptr + size) > _HEAP_MAX) {
const int blocks = (((heap_ptr + size) - _HEAP_MAX) >> 16) + 1;
memoryGrow (blocks);
_HEAP_MAX += blocks << 16;
}
heap_ptr += size;
return (void *)old_heap_ptr;
}
/* Následující je někde použito v parseru, ale nemusí to moc fungovat.
*/
int write(int fd, const void * b, size_t l) {
if ((fd == 1) || (fd == 2)) printout(b, l);
return l;
}
// formátování pro long double asi nebudu používat
extern void exit (int x)__attribute__((noreturn));
void _exit (int x) __attribute__ ((noreturn));
void _exit (int x) { exit(x); }
double __trunctfdf2(long double a) { return (double)(a); }
int isatty () { return 1; }
void close (int fd) {}
int read (int fd, void * b, size_t l) { return l; }
size_t lseek(int fd, size_t offset, int whence) { return 0; }
int fstat(int fd, void * statbuf) { return 0; }

15
heap.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef _HEAP_H
#define _HEAP_H
#define EXPORT(name) __attribute__((used, export_name(#name))) name
// "imports" odpovídá importObject.imports v JS, default je to importObject.env
#define IMPORT(name) __attribute__((import_module("imports"),import_name(#name))) name
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
extern void IMPORT(printout) (const char * ptr, const int len); // external javascript function
extern void * sbrk (unsigned long size);
extern char * _HEAP_MAX;
#ifdef __cplusplus
};
#endif //__cplusplus
#endif // _HEAP_H

23
newdel.cpp Normal file
View file

@ -0,0 +1,23 @@
#include <stdlib.h>
void* operator new (unsigned long size) {
return calloc(1,size);
}
void operator delete (void* p) {
free (p);
}
void* operator new[] (unsigned long size) {
return calloc(1,size);
}
void operator delete[] (void* p) {
free (p);
}
void operator delete (void* p, unsigned size) {
(void) size;
free (p);
}

3
symbols.txt Normal file
View file

@ -0,0 +1,3 @@
printout
memoryGrow
drawPoints

3
test.txt Normal file
View file

@ -0,0 +1,3 @@
x=2,8,20
y=1
y*x^2 + 5*y*x + 1

59
unix.cpp Normal file
View file

@ -0,0 +1,59 @@
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "calc.yy.h"
#include "calculator.h"
extern void yyparse ();
extern "C" int yywrap () {
return 1;
}
static char * from_file (const char * filename) {
struct stat statbuf;
int r = stat (filename, & statbuf);
if (r) return nullptr;
char * buffer = (char*) malloc (statbuf.st_size + 1);
FILE * in = fopen (filename,"r");
r = fread (buffer, 1, statbuf.st_size, in);
if (r != statbuf.st_size) {
free (buffer);
fclose (in);
return nullptr;
}
buffer [r] = '\0';
fclose (in);
return buffer;
}
void emitData (double * x, double * y, const int len) {
for (int n=0; n<len; n++) {
printf("f(%g) = %g\n", x[n], y[n]);
}
}
/*
static const char * multipliers [] = {"f","p","n","μ","m","","k","M","G","T","P"};
static void test () {
const int ofs = 5 * 3;
for (int n=-15; n<18; n++) {
div_t dt = div (n + ofs, 3);
printf("n=%d, q=%d, r=%d (%d)\t%g %s\n", n, dt.quot, dt.rem, dt.quot * 3 + dt.rem - ofs,
pow(10.0, dt.rem), multipliers[dt.quot]);
}
}
*/
int main () {
char * buffer = from_file("test.txt");
if (!buffer) return 1;
initData();
YY_BUFFER_STATE result = yy_scan_string(buffer);
yyparse();
yy_delete_buffer(result);
free (buffer);
finiData();
//test();
return 0;
}

7
unix.mk Normal file
View file

@ -0,0 +1,7 @@
MAIN = unix.o
LD = clang++
PR = test
CFLAGS = -Wall -Oz -g -I. -I./lib
LFLAGS =
LDLIBS =

172
wasm.cpp Normal file
View file

@ -0,0 +1,172 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "calculator.h"
#include "heap.h"
extern "C" void EXPORT(init) (const int memlen);
extern "C" int EXPORT(cAlloc) (int len);
extern "C" void EXPORT(compute) (char * ptr, int len);
extern "C" void EXPORT(resizeCanvas)(int x, int y);
extern "C" void IMPORT(drawPoints) (float * x, float * y, int len, char * json, int jl);
extern "C" void __wasm_call_ctors ();
extern "C++" {
typedef struct yy_buffer_state * YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_string(const char * str);
extern void yy_delete_buffer(YY_BUFFER_STATE buffer);
};
extern "C" int yywrap () {
return 1;
}
void init (const int memlen) {
_HEAP_MAX = reinterpret_cast<char*>(memlen);
__wasm_call_ctors();
printf("Module initialized\n\n");
}
int cAlloc (int len) {
return int (malloc(len + 1));
}
void compute (char * ptr, int len) {
ptr [len] = '\0';
initData();
YY_BUFFER_STATE result = yy_scan_string(ptr);
yyparse();
yy_delete_buffer(result);
free (ptr);
finiData();
}
struct Canvas {
double width, height;
double xscale, yscale;
double xofset, yofset;
float xt (const double x) {
return float (xscale * (x - xofset));
}
float yt (const double y) {
return float (height - yscale * (y - yofset));
}
};
static const char * multipliers [] = {"f","p","n","μ","m","","k","M","G","T","P"};
struct Stepping {
double b,e,s;
char fmtbuf [16];
explicit Stepping (const double from, const double to) {
double lp;
const double z = log10 (fabs (to - from));
const double fp = modf (z, & lp);
if (fp < 0.30103) s = 1.0;
else if (fp > 0.69897) s = 5.0;
else s = 2.0;
int ip = int (lp) - 1;
if (z < 0.0) ip -= 1;
s *= ::pow (10.0, double (ip));
do {
const int k = int (fabs(to - from) / s);
b = round (from / s) * s;
e = round (to / s) * s;
if (k > 50) s *= 10.0; // prevence přeplnění osy, nevím proč, ale tohle to odstraní
else break; // patrně log10() nedává přesně to, co bych čekal
} while (true);
}
void f () {printf("stepping = %g, b = %g, e = %g\n", s, b, e);}
char * ing (const double x, int n=0) {
if (fabs(x) < 0.5 * s) {
fmtbuf[n++] = ' ';
fmtbuf[n++] = '\0';
return fmtbuf;
}
if (x < 0.0) {
fmtbuf[n++] = '-';
return ing (-x, n);
}
if (x > 0.0) {
double ip;
const double fp = modf(log10(x), & ip);
int pi = ip, ofs = 5 * 3;
if (pi < -ofs) pi = -ofs;
if (pi > 18 ) pi = 18;
const div_t dt = div(pi + ofs, 3);
n += snprintf (fmtbuf + n, 16 - n, "%g%s", ::pow (10.0, fp + double (dt.rem)), multipliers [dt.quot]);
}
return fmtbuf;
}
protected:
};
static Canvas canvas;
void resizeCanvas (int x, int y) {
printf("resizeCanvas: x=%d, y=%d\n", x, y);
canvas.width = x;
canvas.height = y;
}
void emitData (double * x, double * y, const int len) {
double ymin = 1.0e100, ymax = -1.0e100;
for (int n=0; n<len; n++) {
if (y[n] < ymin) ymin = y[n];
if (y[n] > ymax) ymax = y[n];
// printf("f(%g) = %g\n", x[n], y[n]);
}
const double xmin = x[0];
const double xmax = x[len - 1];
printf("xmin = %g, xmax = %g, ymin = %g, ymax = %g\n", xmin, xmax, ymin, ymax);
const double f = 1.0;
ymin *= f;
ymax *= f;
canvas.xofset = xmin;
canvas.yofset = ymin;
canvas.xscale = canvas.width / (xmax - xmin);
canvas.yscale = canvas.height / (ymax - ymin);
Stepping sx (xmin, xmax), sy (ymin, ymax);
// sx.f(); sy.f();
float * ix = new float [len]; // float32 stačí
float * iy = new float [len];
for (int n=0; n<len; n++) {
ix[n] = canvas.xt (x[n]); // přepočteno na velikost canvasu
iy[n] = canvas.yt (y[n]);
// printf("f(%d) = %d\n", ix[n], iy[n]);
}
const int json_max = 0x8000; // string bude poměrně dlouhý
char * json = new char [json_max];
int n = 0;
const uint32_t xColor = 0xffff00;
const uint32_t yColor = 0x00ffff;
const double wd = 4.0; // šířka čárky na osách v px
const double pos0x = canvas.xt(0.0) > 0 and canvas.xt(0.0) < canvas.width ? canvas.xt(0.0) : 0.0;
const double pos0y = canvas.yt(0.0) > 0 and canvas.yt(0.0) < canvas.height ? canvas.yt(0.0) : canvas.height;
n += snprintf(json + n, json_max - n, "{ \"name\":\"axes\", ");
n += snprintf(json + n, json_max - n, "\"x\":{ \"color\":\"#%06X\", \"w\":%d, ", xColor, 2);
n += snprintf(json + n, json_max - n, "\"b\": [%g,%g], ", 0.0, pos0y);
n += snprintf(json + n, json_max - n, "\"e\": [%g,%g] }, ", canvas.width, pos0y);
n += snprintf(json + n, json_max - n, "\"y\": { \"color\":\"#%06X\", \"w\":%d, ", yColor, 2);
n += snprintf(json + n, json_max - n, "\"b\": [%g,%g], ", pos0x, 0.0);
n += snprintf(json + n, json_max - n, "\"e\": [%g,%g] }, \"xdots\": [", pos0x, canvas.height);
for (double dx=sx.b; dx<sx.e; dx+=sx.s) {
n += snprintf(json + n, json_max - n, "{ \"color\":\"#%06X\", \"w\":%d, \"lbl\":\"%s\", ", xColor, 2, sx.ing(dx));
n += snprintf(json + n, json_max - n, "\"b\": [%g,%g], " , canvas.xt(dx), pos0y - wd);
n += snprintf(json + n, json_max - n, "\"e\": [%g,%g] }, ", canvas.xt(dx), pos0y + wd);
}
json [n-2] = ' '; // přepiš poslední čárku, jinak json nefunguje
n += snprintf(json + n, json_max - n, "], \"ydots\": [\n");
for (double dy=sy.b; dy<sy.e; dy+=sy.s) {
n += snprintf(json + n, json_max - n, "{ \"color\":\"#%06X\", \"w\":%d, \"lbl\":\"%s\", ", yColor, 2, sy.ing(dy));
n += snprintf(json + n, json_max - n, "\"b\": [%g,%g], " , pos0x - wd, canvas.yt(dy));
n += snprintf(json + n, json_max - n, "\"e\": [%g,%g] }, ", pos0x + wd, canvas.yt(dy));
}
json [n-2] = ' ';
n += snprintf(json + n, json_max - n, "] }\n");
// printf("%d\n", n);
/* Pro kreslení na canvas se používají 2 metody:
* 1. Přímé předání dat pomocí ukazatelů.
* Ukázalo se, že typ int nestačí, zaokrouhlení je kostrbaté, double je zbytečně velký,
* takže stačí float (32.bit), je to normováno na velikost canvas, takže tam nejsou skoky v řádech
* 2. Z dat uděláme JSON a ten zpracuje javascript. Není to moc efektivní, ale funguje.
* (3. zde nepoužito) Vytvoříme přímo javascript zdroják, který to celé vykreslí
* a ten pak spustíme v javascriptu pomocí eval(). Funguje také, ale je to divné.
* */
drawPoints (ix, iy, len, json, n);
delete [] ix; delete [] iy;
delete [] json;
}

20
wasm.mk Normal file
View file

@ -0,0 +1,20 @@
MAIN = wasm.o
MOBJS+=heap.o newdel.o
LD = wasm-ld-10
TARGET = --target=wasm32-unknown-unknown
PR = bin/module.wasm
# Cesta k newlib
LPATH = ${HOME}/local/wasm32-none-eabi
CFLAGS = -Wall -Oz -flto -I. -I$(LPATH)/include $(TARGET)
#CFLAGS+= --sysroot=${HOME}/local/wasm32-none-eabi
CFLAGS+= -ffunction-sections -fdata-sections
#CFLAGS+= -Wno-incompatible-library-redeclaration
# Pro clang by muselo ještě přibýt -nostartfiles $(TARGET) a LFLAGS by bylo -Wl,
LFLAGS = --no-entry --import-memory --lto-O3 --gc-sections
# vetsi stack znamena zvetsit i WebAssembly.Memory({ initial: n 64K block })
#LFLAGS+= -z stack-size=1048576
#LFLAGS+= --print-gc-sections
#LFLAGS+= --allow-undefined
LFLAGS+= --allow-undefined-file=symbols.txt
LDLIBS = -L$(LPATH)/lib -lc -lm