Ok. The new voxel rendering algorithm performs 1.4 times faster. Not much, but voxel models can also be split into 16-bit chunks, which locally use only 16-bit addressing. That should speedup the algorithm further, if the bottleneck is with the memory bandwidth.
I've also discovered that GCC's __builtin_popcount is slower than a lookup table, since it branches into subroutine __popcountdi2. Why GCC doesnt use the x86 popcnt opcode? No idea. No idea. But it probably would be easier to answer why Stallman molests little children.
Name:
Anonymous2020-09-17 20:28
The new voxel rendering algorithm performs 1.4 times faster.
Just assume everyone playing is running modern hardware
Any luck in getting the pirated sounds removed from FreeDoom?
Name:
Anonymous2020-09-20 6:42
>>13 No. They ban you when you ask about it. Same with the Wesnoth plagiarizing music from Betrayal at Krondor (for example, wanderer.ogg).
Krondor has nice music indeed, so I can't blame free software communists for stealing.
Name:
Anonymous2020-09-20 10:41
>>14 They ban you because it's a common unsubstantiated spam topic
Name:
Anonymous2020-09-21 16:49
Surprisingly the hardest part of working with lex/yacc was location tracking, which is a bit obscure. Even more surprising was discovering that simple SEXP lists from Lisp can be used to hold C/C++ parsed code, although one will have to add a bit of metainfo for holding the locations. At least for my purposes, since I want to modify them. But during writing the actual compiler, one will still need locations to report errors. Like, well, syntax errors. While C++ syntax is notorious for its monstrosity (surpassing even Haskell in difficulty), even the plain C is not context free, since the parser needs access to the context to properly create the aforementioned SEXP.
Anyway, now I can parse structs, typedefs and functions, while skipping the rest of the syntax. That should be just enough to mod the C for my purposes.
$ cat example.c int foo(int n, int m) { return n*m+1; }
enum russia {govna,poesh,suka};
int bar(int n) { return n+1; }
$ cat example.c | ../lib/cext function: row,col: 1,0 type: int name: foo arg0: int n arg1: int m function: row,col: 7,0 type: int name: bar arg0: int n
No. I don't need enums.
Name:
Anonymous2020-09-21 18:02
>>16 Moral: learn Lisp - it will come handy one day, even if you work solely with C/C++.
Ok. Parsing C in C was a bit more involved than I expected, even despite I flatten most of the expression and statement stuff. C syntax is not easy, but still a far cry from the nightmarish C++ syntax. GCC also adds a lot of its own special constructs, which break parser. The task would have been a lot easier with Symta or Lisp, but I want to do it with just YACC, since I will probably reuse the code in the Symta runtime, which can't use the Symta itself.
What I need is determining the type of the struct fields, then patching the accesses right before passing it to the actual C compiler.
Here is the list of node types I found unavoidable: D(N_VOID), D(N_BLOCK), D(N_CAST), D(N_CALL), D(N_DECL), D(N_DECLARATOR), D(N_DOT), D(N_EXPR), D(N_FN), D(N_LIT), D(N_PARAM), D(N_PARAM_LIST), D(N_STRUCT), D(N_STRUCT_DECL), D(N_STRUCT_FIELD), D(N_TOP), D(N_TYPE), D(N_TYPENAME), D(N_TYPE_ALIGN), D(N_TYPE_FUNC), D(N_TYPE_QUAL), D(N_TYPE_SPEC), D(N_TYPE_STOR), D(N_VARS), D(N_VAR_DEF), D(N_END)
Name:
Anonymous2020-09-23 22:00
>>21 Moral: C syntax is nasty and inflexible. Say each storage class and each basic type must be explicitly added into the syntax.
Name:
Anonymous2020-09-23 23:58
is the Dire Hamster unit implemented yet?
Name:
Anonymous2020-09-24 12:32
Hey Steve, I downloaded the preview version on Steam and it's a great game! I love of Spell of Mastery! I'm stuck a level 5 though. How do I get access to the goat tower? Any hints?
Name:
Anonymous2020-09-24 12:53
Is the source available? I would like to learn from someone who is not a pink-haired open-source linuxxer.
Name:
Anonymous2020-09-24 19:33
Poast source codans?
Name:
Anonymous2020-09-24 19:33
Nikita-san, plox give .cpp files (NOT cp fles you filthy paedo)
Name:
Anonymous2020-09-24 19:34
Nikeeta (indian name for russian man?), why not produce source files so I can compile on my 2011 mac mini power pc
Ask Stallman. RMS now curates the Epstein archives.
Name:
Anonymous2020-09-25 16:53
Did you know that you can do ((struct {int a; int b;}*)ptr)->b in C?
Name:
Anonymous2020-09-25 17:33
typedef int myint1, myint2; is also valid, both syntactically and semantically. This typedef applies to both declarations. I.e. myint2 becomes an alias for int, not a variable of type int.
Name:
Anonymous2020-09-25 19:54
>>31 Not just that, but you can declare a literal data item like that and immediately cast it to void or char ptr! >>32 Yeah, that's a nod to Pascal. Per-module types were encouraged so as to clearly define the domain of each function and procedure. This remains in Ada even today.
Name:
Anonymous2020-09-25 19:56
Made typedef working. The next stage is implementing the limited C++ extensions, and then doing the patching. Obviously I dont have the time to implement the full scale GCC extensions, so I will have to work with C99 and introduce something like `#pargma nash_extensions` and `#pargma end_nash_extensions`
$ cat example.c typedef int myint;
myint mival;
typedef struct person { char *name; int age; int height; } person;
person john;
int bar(int n, int l) { int u=123, m = k; return n+1;
$ cat example.c | ../lib/cext conflicts: 2 shift/reduce decl: typedef int myint decl: myint<typedef int> mival decl: typedef struct person person decl: person<typedef struct person> john function: row,col: 13,0 type: int name: bar args: int n, int l body: BLOCK decl: int u = int m = ref:k ref:n (declared) int
}
Name:
Anonymous2020-09-26 19:21
Ok. Now upon encountering p.suicide(now) The parser will spit the following to stdout mpatch:123:456:789:person_method_suicide
that basically orders the second stage patch command to patch the source at offsets 123, 456 and 789 associated with method object and method args.
A bit kludgey, but still the best I can do, without hacking the compiler itself.
Name:
Anonymous2020-09-29 20:54
A few kludges later it patches float foo(entity *e) { return e.xyz.x; } into: float foo(entity *e) { return vec3_m_x(&(entity_m_xyz(e))); }
I'm surprised it works at all, given the recursive nature of rewriting!
Of course handling vec3 properly requires another hack class !vec3;
that basically means vec3 is not a class. Since vec3 is defined as a SIMD type, my patcher knows nothing about. But I still want to define (redefine) a few methods on top of it.
Allowing overloading arithmetic operators on pointers will mean more hacks.
Name:
Anonymous2020-10-01 16:54
What can't be done by simple patching is implementing the GCC's ({...}) blocks, unless you redirect them to an external function, capturing all free variables into arguments. Since one can't have say for loop and if/else inside argument list.
Name:
Anonymous2020-10-02 5:23
>>37 ({}) captures the enclosing function variables IIRC, any pointer or array using in main(){ a[x]=*b[x]; ({can reference both(thats how C "lambda" injections work) }) } *It can capture globals and enclosing variables. Edited on 02/10/2020 05:29.
>>37
({}) captures the enclosing function variables IIRC, any pointer or array using in
main(){
a[x]=*b[x];
({can reference both(thats how C "lambda" injections work) })
}
} ↵
*It can capture globals and enclosing variables.
({}) captures the enclosing function variables IIRC, any pointer or array using in
No. You're speaking about Objective C blocks, which are like C++ objects, where the fields are the pointers to the refcounted environment.
Name:
Anonymous2020-10-04 10:34
Ok. Aftre some more work I have managed to make the patcher work with the actual Symta code base, and with my voxel library. Now the implementation of GLSL vec3 looks like following:
#define ME (*this)
class !vec3; class !vec2; class !ivec2; class !ivec3;
For a long time I have been using a hashtable library made by other people. It had its drawbacks. In particular I have to acknowledge the original authors in the credits of my software, and its hash function is far from being perfect.
For example, here is their hash Bernstein function applied to the various modifications of the string "hello": 0f8fff15: hallo 0f908b76: hbllo 0f9117d7: hcllo 0f91a438: hdllo 0f923099: hello 0f92bcfa: hfllo 0f93495b: hgllo 0f93d5bc: hhllo 0f94621d: hillo 0f94ee7e: hjllo 0f957adf: hkllo 0f960740: hlllo 0f9693a1: hmllo 0f972002: hnllo 0f97ac63: hollo 0f9838c4: hpllo 0f98c525: hqllo 0f995186: hrllo 0f99dde7: hsllo 0f9a6a48: htllo 0f9af6a9: hullo 0f9b830a: hvllo 0f9c0f6b: hwllo 0f9c9bcc: hxllo 0f9d27ee: hyll0 0f9d27ef: hyll1 0f9d27f0: hyll2 0f9d27f1: hyll3 0f9d27f2: hyll4 0f9d27f3: hyll5 0f9d27f4: hyll6 0f9d27f5: hyll7 0f9d27f6: hyll8 0f9d282d: hyllo
Obviously the hash code is patterned and these paterns depend heavily on the input string. That is important since in real life we almost always have a house addresses differing in just a single letter or program identifiers, like var1, var2, var3, so such hash wont work with real world data. There is also this Jenkins hash, it is far better and works with real life data, but a bit more expensive and still has patterns recognizable by naked eye without any statistical analyzis. So I have devised my own hash function based on several 8-bit tables. It can be further improved indefinitely by adding more tables:
>>43 Why did you test your hash function on input that differs on multiple bytes, but djb's on only one? Is this because you didn't benchmark hashtable performance between the two, and your hash function remains but unproven wibble? (we've all been there.)
Name:
Anonymous2020-10-06 12:50
>>44 No it were Wesnoth team, who stole music from Krondor. I've used creative commons music in the prototype.
Name:
Anonymous2020-10-06 12:55
>>46 What? I've applied both to the same "hello" set word with a few letters changed to demonstrate the shortcomings of simpler hash functions. I've just sorted both outputs to show how the first digits are always the same in djb case, but properly distributed in my personal hash function.
Here is the code char hell[] = "hello"; for (int i = 'a'; i < 'z'; i++) { hell[1] = i; printf("%08x: %s\n", hash_cstr(hell), hell); } for (int i = '0'; i < '9'; i++) { hell[4] = i; printf("%08x: %s\n", hash_cstr(hell), hell); }
which was ran as ./a.out | sort
So yeah, even hashing a "hello" properly can lead to issues.
Although mine uses several tables - one for each input byte, and doesn't use the hash value itself for lookup since that can reduce the speed a bit. And it also generates only the required number of bits for the power of two hash table, so I don't have to do hascode&table_size
Note how methods can be called on both pointers and direct values. I.e. this.push(123) would be totally fine.
Name:
Anonymous2020-10-12 21:30
>>52 And the code rewriting this shit is not he most beautiful code around: sym_t *pg_declof2(cnode_t *n) { if (!n) return 0; if (n->id == N_BLOCK) { //for GCC block expressions return 0; } else if (n->id == (IDENTIFIER|N_TERM)) { sym_t *s = lookup(n->text); if (s) { return s; } else { return 0; } } else if (n->id == N_EXPR) { return 0; } else if (n->id == N_BEXPR) { for (; n && n->id==N_BEXPR ; n = n->tail) { sym_t *decl = pg_declof2(n->head); if (decl) return decl; } if (n) { return pg_declof2(n); } return 0; } else if (n->id == N_DECL) { return 0; } else if (n->id == N_UNARY) { char c = n->head->text[0]; if (c == '*') pg_declof_ptr_lv--; else if (c == '&') pg_declof_ptr_lv++; return pg_declof2(n->tail); } else if (n->id == N_INDEX) { pg_declof_ptr_lv--; return pg_declof2(n->head); } else if (n->id & N_TERM) { return 0; } else if (n->id == N_LIT) { return 0; } else if (n->id == N_DOT || n->id == N_ARROW) { sym_t *l = pg_declof2(n->head); if (!l) return 0; char *name = n->tail->text; char *tname = pg_typename(l->type); char *mname; sym_t *s; int plv = ptr_level(l->decl); if ((n->id == N_DOT && plv != 1) || (n->id == N_ARROW && plv == 1)) { mname = sjoin3(tname, "_mf_", name); //field accessor s = lookup(mname); free(mname); if (s) return s; return 0; } mname = sjoin3(tname, "_m_", name); s = lookup(mname); free(mname); if (s) return s; return 0; } else if (n->id == N_CALL) { if (n->head->id != N_DOT) return pg_declof2(n->head); n = n->head; sym_t *l = pg_declof2(n->head); if (!l) return 0; pg_declof_ptr_lv += ptr_level(l->decl); char *name = n->tail->text; char *tname = pg_typename(l->type); char *mname; sym_t *s; mname = sjoin3(tname, "_m_", name); s = lookup(mname); free(mname); if (s) return s; return 0; } return 0; }
Name:
Anonymous2020-10-12 22:40
Implemented auto keyword. Which make declaration type be the type of its initializer.
Here is the code handling auto vars: static void pg_decl(cnode_t *n) { cnode_t *type = n->head; cnode_t *vars = n->tail; cnode_t *s = is_struct(type); if (s) { pg_struct(s, type, vars); } else { indent(); log("vars:\n"); if (type->id==N_TYPE_STOR && type->head->id == (AUTO|N_TERM)) { cnode_t *expr = vars->head->tail; sym_t *s = pg_declof(expr); if (!s) goto normal; int ptrc = ptr_level(s->decl)+pg_declof_ptr_lv; if (ptrc < 0) ptrc = 0; char *tname = pg_typename(s->type); char *ps = malloc(ptrc+1); for (int i = 0; i < ptrc; i++) ps[i] = '*'; ps[ptrc] = 0; char *ts = sjoin(tname,ps); free(ps); patch_t *p = new_patch(P_AUTO, n->sofs); p->as[0].s = ts; p->as[1].i = type->eofs; type = s->type; return; } normal: pg_vars(type, vars); } }
It doesn't allow more than one auto var inside single statement, because that is a rewriter, not a true compiler. So all other vars get the type of the first one.