first commit
This commit is contained in:
parent
50d0a2c3c9
commit
c8d704a06c
18 changed files with 871 additions and 0 deletions
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
# kdevelop
|
||||
.kde*
|
||||
*.kdev4
|
||||
# other
|
||||
*.zip
|
||||
*.wasm
|
||||
*.lst
|
||||
*.map
|
||||
*.o
|
33
Makefile
Normal file
33
Makefile
Normal 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
46
bin/index.html
Normal 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
126
bin/index.js
Normal 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
7
bin/test.txt
Normal 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
31
calc.l
Normal 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
68
calc.y
Normal 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
82
calculator.cpp
Normal 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
121
calculator.h
Normal 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
46
heap.c
Normal 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
15
heap.h
Normal 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
23
newdel.cpp
Normal 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
3
symbols.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
printout
|
||||
memoryGrow
|
||||
drawPoints
|
3
test.txt
Normal file
3
test.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
x=2,8,20
|
||||
y=1
|
||||
y*x^2 + 5*y*x + 1
|
59
unix.cpp
Normal file
59
unix.cpp
Normal 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
7
unix.mk
Normal 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
172
wasm.cpp
Normal 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
20
wasm.mk
Normal 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
|
||||
|
Loading…
Reference in a new issue