2023-05-18 15:34:05 +00:00
|
|
|
#include <AutoreleasePool.h>
|
2023-05-27 03:39:13 +00:00
|
|
|
#include <hash.h>
|
|
|
|
#include <qfile.h>
|
|
|
|
#include <script.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2023-05-18 15:34:05 +00:00
|
|
|
#include "algebra.h"
|
|
|
|
#include "basisblade.h"
|
|
|
|
#include "basisgroup.h"
|
|
|
|
#include "metric.h"
|
|
|
|
#include "multivector.h"
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
@static AutoreleasePool *autorelease_pool;
|
|
|
|
@static void
|
|
|
|
arp_start (void)
|
|
|
|
{
|
|
|
|
autorelease_pool = [[AutoreleasePool alloc] init];
|
|
|
|
}
|
|
|
|
|
|
|
|
@static void
|
|
|
|
arp_end (void)
|
|
|
|
{
|
|
|
|
[autorelease_pool release];
|
|
|
|
autorelease_pool = nil;
|
|
|
|
}
|
|
|
|
|
2023-05-27 03:39:13 +00:00
|
|
|
static void
|
|
|
|
basic_test (void)
|
2023-05-18 15:34:05 +00:00
|
|
|
{
|
|
|
|
arp_start ();
|
|
|
|
|
|
|
|
BasisBlade *a = [[BasisBlade basis:1] retain];
|
|
|
|
BasisBlade *b = [[BasisBlade basis:2] retain];
|
|
|
|
BasisBlade *c = [[BasisBlade basis:4] retain];
|
|
|
|
BasisBlade *d = [[BasisBlade basis:8] retain];
|
|
|
|
BasisBlade *blades[] = {a, b, c, d};
|
|
|
|
static string names[] = {"a", "b", "c", "d"};
|
|
|
|
|
|
|
|
// printf ("a: %@\n", a);
|
|
|
|
// printf ("b: %@\n", b);
|
|
|
|
// printf ("c: %@\n", c);
|
|
|
|
// printf ("d: %@\n", d);
|
|
|
|
|
|
|
|
arp_end ();
|
|
|
|
#if 0
|
|
|
|
arp_start ();
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
arp_end ();
|
|
|
|
arp_start ();
|
|
|
|
BasisBlade *vec = blades[i];
|
|
|
|
printf ("%s: %@\n", names[i], vec);
|
|
|
|
for (int j = 0; j < 4; j++) {
|
|
|
|
BasisBlade *bvec = [vec outerProduct:blades[j]];
|
|
|
|
if (![bvec scale]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
printf ("%s^%s: %@\n", names[i], names[j], bvec);
|
|
|
|
for (int k = 0; k < 4; k++) {
|
|
|
|
BasisBlade *tvec = [bvec outerProduct:blades[k]];
|
|
|
|
if (![tvec scale]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
printf ("%s^%s^%s: %@\n", names[i], names[j], names[k],
|
|
|
|
tvec);
|
|
|
|
for (int l = 0; l < 4; l++) {
|
|
|
|
BasisBlade *qvec = [tvec outerProduct:blades[l]];
|
|
|
|
if (![qvec scale]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
printf ("%s^%s^%s^%s: %@\n",
|
|
|
|
names[i], names[j], names[k], names[l],
|
|
|
|
qvec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arp_end ();
|
|
|
|
#endif
|
|
|
|
arp_start ();
|
|
|
|
|
|
|
|
Metric *m = [Metric R:3,0,1];
|
|
|
|
BasisBlade *ad = [a geometricProduct:d metric:m];
|
|
|
|
BasisBlade *prod = [ad geometricProduct:ad metric:m];
|
|
|
|
printf ("%s%s %s%s: %@\n",
|
|
|
|
names[0], names[3], names[0], names[3], prod);
|
|
|
|
|
|
|
|
Algebra *alg = [Algebra R:3, 0, 1];
|
2023-05-21 13:20:38 +00:00
|
|
|
double plane1_vals[4] = {1, 0, 0, 8};
|
|
|
|
double plane2_vals[4] = {0, 1, 0, 8};
|
|
|
|
double origin_vals[4] = {0, 0, 0, 1};
|
|
|
|
MultiVector *plane1 = [alg group:0 values:plane1_vals];
|
|
|
|
MultiVector *plane2 = [alg group:0 values:plane2_vals];
|
2023-05-19 02:15:29 +00:00
|
|
|
MultiVector *origin = [alg group:3 values:origin_vals];
|
2023-05-18 15:34:05 +00:00
|
|
|
|
|
|
|
MultiVector *line = [plane1 wedge:plane2];
|
2023-05-24 09:25:47 +00:00
|
|
|
MultiVector *point = [[line dot:origin] product:[line reverse]];
|
2023-05-18 15:34:05 +00:00
|
|
|
printf ("plane1:%@\nplane2:%@\nline:%@\norigin:%@\n", plane1, plane2, line, origin);
|
|
|
|
printf ("point:%@\n", point);
|
|
|
|
|
|
|
|
arp_end ();
|
2023-05-27 03:39:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct var_s {
|
|
|
|
string name;
|
|
|
|
MultiVector *value;
|
|
|
|
} var_t;
|
|
|
|
|
|
|
|
static hashtab_t *symtab;
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
EOF, SEMI,
|
|
|
|
VAR, EQUAL,
|
|
|
|
ID, VALUE,
|
|
|
|
OPENP, CLOSEP,
|
|
|
|
OPENB, CLOSEB,
|
|
|
|
OPENS, CLOSES,
|
|
|
|
MUL, DIV, PLUS, MINUS,
|
|
|
|
WEDGE, ANTIWEDGE, DOT,
|
2023-05-29 00:52:10 +00:00
|
|
|
REVERSE, DUAL,
|
2023-05-27 03:39:13 +00:00
|
|
|
} token_e;
|
|
|
|
|
|
|
|
script_t script;
|
|
|
|
string token_str;
|
|
|
|
token_e lookahead = -1;
|
|
|
|
|
|
|
|
typedef struct token_s {
|
|
|
|
token_e id;
|
|
|
|
union {
|
|
|
|
MultiVector *value;
|
|
|
|
string name;
|
|
|
|
};
|
|
|
|
} token_t;
|
|
|
|
|
|
|
|
static token_t
|
|
|
|
get_token ()
|
|
|
|
{
|
|
|
|
if (!Script_TokenAvailable (script, 1)) {
|
|
|
|
return {EOF, nil};
|
|
|
|
}
|
|
|
|
Script_GetToken (script, 1);
|
|
|
|
switch (token_str) {
|
|
|
|
case "var": return {VAR, nil};
|
|
|
|
case "=": return {EQUAL, nil};
|
|
|
|
case ";": return {SEMI, nil};
|
|
|
|
case "(": return {OPENP, nil};
|
|
|
|
case ")": return {CLOSEP, nil};
|
|
|
|
case "[": return {OPENB, nil};
|
|
|
|
case "]": return {CLOSEB, nil};
|
|
|
|
case "{": return {OPENS, nil};
|
|
|
|
case "}": return {CLOSES, nil};
|
|
|
|
case "*": return {MUL, nil};
|
|
|
|
case "/": return {DIV, nil};
|
|
|
|
case "+": return {PLUS, nil};
|
|
|
|
case "-": return {MINUS, nil};
|
|
|
|
case "^": return {WEDGE, nil};
|
|
|
|
case "&": return {ANTIWEDGE, nil};
|
|
|
|
case ".": return {DOT, nil};
|
|
|
|
case "~": return {REVERSE, nil};
|
2023-05-29 00:52:10 +00:00
|
|
|
case "!": return {DUAL, nil};
|
2023-05-27 03:39:13 +00:00
|
|
|
}
|
|
|
|
return {ID, .name = token_str };
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
syntax_error ()
|
|
|
|
{
|
2023-05-29 00:52:10 +00:00
|
|
|
obj_error (nil, 0, "syntax error before `%s': %d\n", token_str,
|
|
|
|
Script_GetLine (script));
|
2023-05-27 03:39:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
match (token_e token)
|
|
|
|
{
|
|
|
|
if (lookahead == -1) {
|
|
|
|
lookahead = get_token ().id;
|
|
|
|
}
|
|
|
|
return token == lookahead;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
advance ()
|
|
|
|
{
|
|
|
|
lookahead = get_token ().id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Algebra *algebra;
|
|
|
|
static MultiVector *minus_one;
|
|
|
|
static MultiVector *expression ();
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_digit (string x)
|
|
|
|
{
|
|
|
|
return (x == "0" || x == "1" || x == "2" || x == "3" ||
|
|
|
|
x == "4" || x == "5" || x == "6" || x == "7" ||
|
|
|
|
x == "8" || x == "9");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_number (string x)
|
|
|
|
{
|
|
|
|
return x == "." || is_digit (x);
|
|
|
|
}
|
|
|
|
|
|
|
|
static MultiVector *
|
|
|
|
factor ()
|
|
|
|
{
|
|
|
|
MultiVector *vec;
|
|
|
|
if (match (REVERSE)) {
|
|
|
|
advance ();
|
2023-05-29 00:52:10 +00:00
|
|
|
vec = [factor () reverse];
|
|
|
|
} else if (match (DUAL)) {
|
|
|
|
advance ();
|
|
|
|
vec = [factor () dual];
|
2023-05-27 03:39:13 +00:00
|
|
|
} else if (match (MINUS)) {
|
|
|
|
advance ();
|
2023-05-29 00:52:10 +00:00
|
|
|
vec = [minus_one product:factor ()];
|
2023-05-27 03:39:13 +00:00
|
|
|
} else if (match (OPENP)) {
|
|
|
|
advance ();
|
|
|
|
vec = expression ();
|
|
|
|
if (!match (CLOSEP)) {
|
|
|
|
syntax_error ();
|
|
|
|
}
|
|
|
|
advance ();
|
|
|
|
} else if (match (ID)) {
|
|
|
|
if (is_digit (str_mid (token_str, 0, 1))) {
|
|
|
|
string num_str = nil;
|
|
|
|
string blade_str = nil;
|
|
|
|
int pos = 0;
|
|
|
|
while (is_number (str_mid (token_str, pos, pos + 1))) {
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
num_str = str_mid (token_str, 0, pos);
|
|
|
|
if (str_mid (token_str, pos, pos + 1) == "e") {
|
|
|
|
blade_str = str_mid (token_str, ++pos);
|
|
|
|
}
|
|
|
|
BasisBlade *blade = [BasisBlade basis:0];
|
|
|
|
pos = 0;
|
|
|
|
while (is_digit (str_mid (blade_str, pos, pos + 1))) {
|
2023-06-08 03:56:23 +00:00
|
|
|
int x = str_char (blade_str, pos++) - '0';
|
2023-05-27 03:39:13 +00:00
|
|
|
BasisBlade *new = [BasisBlade basis:1 << x];
|
|
|
|
blade = [blade outerProduct:new];
|
|
|
|
}
|
|
|
|
|
|
|
|
double num = strtod (num_str, nil) * [blade scale];
|
|
|
|
vec = [algebra ofGrade:[blade grade]];
|
|
|
|
*[vec componentFor:blade] = num;
|
|
|
|
} else {
|
|
|
|
var_t *var = Hash_Find (symtab, token_str);
|
|
|
|
if (!var) {
|
|
|
|
syntax_error ();
|
|
|
|
}
|
|
|
|
vec = var.value;
|
|
|
|
}
|
|
|
|
advance ();
|
|
|
|
} else {
|
|
|
|
syntax_error ();
|
|
|
|
vec = nil;
|
|
|
|
}
|
|
|
|
return vec;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MultiVector *
|
|
|
|
high_term ()
|
|
|
|
{
|
|
|
|
MultiVector *vec = nil;
|
|
|
|
SEL op = nil;
|
|
|
|
while (1) {
|
|
|
|
if (vec) {
|
|
|
|
vec = [vec performSelector:op withObject: factor()];
|
|
|
|
} else {
|
|
|
|
vec = factor ();
|
|
|
|
}
|
|
|
|
if (match (WEDGE)) {
|
|
|
|
op = @selector(wedge:);
|
|
|
|
advance ();
|
|
|
|
} else if (match (DOT)) {
|
|
|
|
op = @selector(dot:);
|
|
|
|
advance ();
|
|
|
|
} else {
|
|
|
|
return vec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static MultiVector *
|
|
|
|
term ()
|
|
|
|
{
|
|
|
|
MultiVector *vec = nil;
|
|
|
|
SEL op = nil;
|
|
|
|
while (1) {
|
|
|
|
if (vec) {
|
|
|
|
vec = [vec performSelector:op withObject: high_term()];
|
|
|
|
} else {
|
|
|
|
vec = high_term ();
|
|
|
|
}
|
2023-05-29 00:52:10 +00:00
|
|
|
if (match (REVERSE) || match (DUAL) || match (OPENP) || match (ID)) {
|
2023-05-27 03:39:13 +00:00
|
|
|
op = @selector(product:);
|
2023-05-29 00:52:10 +00:00
|
|
|
} else if (match (DIV)) {
|
|
|
|
op = @selector(divide:);
|
|
|
|
advance ();
|
2023-05-27 03:39:13 +00:00
|
|
|
} else {
|
|
|
|
return vec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static MultiVector *
|
|
|
|
expression ()
|
|
|
|
{
|
|
|
|
MultiVector *vec = nil;
|
|
|
|
SEL op = nil;
|
|
|
|
while (1) {
|
|
|
|
if (vec) {
|
|
|
|
vec = [vec performSelector:op withObject: term()];
|
|
|
|
} else {
|
|
|
|
vec = term ();
|
|
|
|
}
|
|
|
|
if (match (PLUS)) {
|
|
|
|
op = @selector(plus:);
|
|
|
|
advance ();
|
|
|
|
} else if (match (MINUS)) {
|
|
|
|
op = @selector(minus:);
|
|
|
|
advance ();
|
|
|
|
} else {
|
|
|
|
return vec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static var_t *
|
|
|
|
assignment ()
|
|
|
|
{
|
|
|
|
if (!match (ID) || is_digit (str_mid (token_str, 0, 1))) {
|
|
|
|
printf ("%s\n", token_str);
|
|
|
|
syntax_error ();
|
|
|
|
}
|
|
|
|
var_t *var = Hash_Find (symtab, token_str);
|
|
|
|
if (!var) {
|
|
|
|
syntax_error ();
|
|
|
|
}
|
|
|
|
advance ();
|
|
|
|
if (!match (EQUAL)) {
|
|
|
|
syntax_error ();
|
|
|
|
}
|
|
|
|
advance ();
|
|
|
|
var.value = [expression () retain];
|
|
|
|
return var;
|
|
|
|
}
|
|
|
|
|
|
|
|
static var_t *
|
|
|
|
declaration ()
|
|
|
|
{
|
|
|
|
if (!match (ID) || is_digit (str_mid (token_str, 0, 1))) {
|
|
|
|
syntax_error ();
|
|
|
|
}
|
|
|
|
if (Hash_Find (symtab, token_str)) {
|
|
|
|
syntax_error ();
|
|
|
|
}
|
|
|
|
var_t *var = obj_malloc (sizeof (var_t));
|
|
|
|
var.name = str_hold (str_unmutable (token_str));
|
|
|
|
Hash_Add (symtab, var);
|
|
|
|
assignment ();
|
|
|
|
return var;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parse_script (string name, QFile file)
|
|
|
|
{
|
|
|
|
script = Script_New ();
|
2023-05-29 06:13:27 +00:00
|
|
|
Script_SetSingle (script, "()[]{}/+-^&~=;!");
|
2023-05-27 03:39:13 +00:00
|
|
|
token_str = Script_FromFile (script, name, file);
|
|
|
|
|
|
|
|
while (!match (EOF)) {
|
|
|
|
arp_end ();
|
|
|
|
arp_start ();
|
|
|
|
var_t *var;
|
|
|
|
if (match (VAR)) {
|
|
|
|
advance ();
|
|
|
|
var = declaration ();
|
|
|
|
printf ("var %s = %@\n", var.name, var.value);
|
|
|
|
} else {
|
|
|
|
var = assignment ();
|
|
|
|
printf ("%s = %@\n", var.name, var.value);
|
|
|
|
}
|
|
|
|
advance ();
|
|
|
|
}
|
|
|
|
|
|
|
|
Script_Delete (script);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static string
|
|
|
|
get_symtab_key (void *var, void *unused)
|
|
|
|
{
|
|
|
|
return ((var_t *) var).name;
|
|
|
|
}
|
|
|
|
|
2023-05-29 06:14:59 +00:00
|
|
|
static Algebra *
|
|
|
|
parse_algebra (string spec)
|
|
|
|
{
|
|
|
|
ivec3 R = {};
|
|
|
|
string s = spec;
|
|
|
|
if (is_digit (str_mid (spec, 0, 1))) {
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
int end = 0;
|
|
|
|
R[i] = strtol (s, &end, 0);
|
|
|
|
string e = str_mid (s, end, end + 1);
|
|
|
|
if (!e) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (e != ",") {
|
|
|
|
goto bad_spec;
|
|
|
|
}
|
|
|
|
s = str_mid (s, end + 1);
|
|
|
|
}
|
|
|
|
if (!R[0] && !R[1] && !R[2]) {
|
|
|
|
goto bad_spec;
|
|
|
|
}
|
|
|
|
return [Algebra R:R[0], R[1], R[2]];
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
bad_spec:
|
|
|
|
printf ("bad algebra spec: %s\n", spec);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2023-05-27 03:39:13 +00:00
|
|
|
int
|
|
|
|
main (int argc, string *argv)
|
|
|
|
{
|
|
|
|
symtab = Hash_NewTable (127, get_symtab_key, nil, nil);
|
|
|
|
if (argc < 2) {
|
|
|
|
basic_test ();
|
|
|
|
} else {
|
|
|
|
arp_start ();
|
|
|
|
algebra = [[Algebra PGA:3] retain];
|
|
|
|
arp_end ();
|
|
|
|
arp_start ();
|
|
|
|
double m1 = -1;
|
|
|
|
minus_one = [[algebra ofGrade:0 values:&m1] retain];
|
|
|
|
arp_end ();
|
|
|
|
arp_start ();
|
|
|
|
for (int i = 1; i < argc; i++) {
|
2023-05-29 06:14:59 +00:00
|
|
|
if (argv[i] == "-a") {
|
|
|
|
Algebra *a = [parse_algebra (argv[++i]) retain];
|
|
|
|
if (!a) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
[algebra release];
|
|
|
|
algebra = a;
|
|
|
|
[minus_one release];
|
|
|
|
minus_one = [[algebra ofGrade:0 values:&m1] retain];
|
|
|
|
continue;
|
|
|
|
}
|
2023-05-27 03:39:13 +00:00
|
|
|
QFile file = Qopen (argv[i], "rt");
|
|
|
|
if (file) {
|
|
|
|
arp_end ();
|
|
|
|
arp_start ();
|
2023-05-29 06:14:59 +00:00
|
|
|
printf ("Using algebra %@\n", algebra);
|
2023-05-27 03:39:13 +00:00
|
|
|
int res = parse_script (argv[i], file);
|
|
|
|
Qclose (file);
|
|
|
|
if (!res) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printf ("%s: failed to open '%s'\n", argv[0], argv[i]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-18 15:34:05 +00:00
|
|
|
return 0;
|
|
|
|
}
|