Re: Misc (semi OT): Well, distractions...

Liste des GroupesRevenir à c arch 
Sujet : Re: Misc (semi OT): Well, distractions...
De : cr88192 (at) *nospam* gmail.com (BGB)
Groupes : comp.arch
Date : 25. Apr 2025, 22:47:08
Autres entêtes
Organisation : A noiseless patient Spider
Message-ID : <vuh049$t91a$1@dont-email.me>
References : 1 2 3 4
User-Agent : Mozilla Thunderbird
On 4/25/2025 7:36 AM, Robert Finch wrote:
On 2025-04-24 11:17 p.m., BGB wrote:
On 4/24/2025 6:00 PM, MitchAlsup1 wrote:
On Thu, 24 Apr 2025 20:10:29 +0000, BGB wrote:
>
----------
>
>
But, a recent line of fiddling has gone in an odd direction.
   I felt a need for a basic script language for some tasks;
>
I have been doing something similar--except I wrote my script
translator in eXcel.
>
>
Errm?...
>
I use it to read *.h files and spit out *.c files that translate
type mathfunction(type arguments) into a series of lines Brian's
compiler interprets as transcendental instructions in My 66000
ISA.
>
So, code contains the prototype::
>
extern type_r recognized_name( type_a1 name );
>
and my scripter punts out::
>
type_r recognized_spelling( type_a name )
{
     register typoe_a __asm__("R1") = name;
     __asm__("instruction_spelling\t%4,%4" : "=r" (R1) : "r" (r1) );
     return R1;
}
>
So when user codes (in visibility of math.h)
>
     y = sinpi( x );
>
compiler spits out:
>
     SINPI    Ry,Rx
>
OK.
>
>
>
In my case, the script interpreter is written in C.
   Code needed to get the core interpreter working: Around 1000 lines;
   Code needed after adding more stuff, around 1500 lines.
Though, not counting the ~ 600 lines needed mostly for the dynamic type- system and similar.
>
A vaguely similar design was implemented inside the TestKern shell, but was written to make use of BGBCC extensions. For this case, needed to write something that would also work in MSVC and GCC. Core design for the dialect was still similar though (and chose BASIC as a base partly becuase I already knew I could get something that was fairly usable with comparably small code).
>
>
Language sort of looks like:
   //comment, contents entirely ignored by parser
   rem stuff  //also comment, but subject to token rules
   x=a+b      //basic assignment
   let x=a+b  //also assignment, creates vars in global scope
   temp y=a+b //similar to let, but dynamically scoped
   x=a*b+c*d  //does compound statements with a normal-ish precedence.
   x=12345    //integer literal, decimal
   x=0x1234   //hexadecimal
   x="string"  //string, uses C style escapes
   dim a(128)  //creates a global array
   if x<10 goto label
   label:
   goto label   //goto
   gosub label  //subroutine call to label
   return       //return from most recent gosub
   end          //script terminates
   print stuff  //print stuff to console
   x=arr(i)     //load from array
   arr(i)=x     //store to array
>
Atypical stuff:
   Dynamically typed;
     Traditional BASIC used suffixes to encode type.
     With no-suffix typically for a default numeric type.
     QBasic and Visual Basic using static types.
   Dynamically scoped;
     Like Emacs Lisp.
     Callee can see variables in the caller;
     Variables can be created that do not effect caller.
   ...
>
Atypical syntax:
   x = gosub label a=3, b=4  //gosub with return values and parameters.
   return expr    //return with expression
   v=(vec 1,2,3)  //vector type
   m=(vec (vec 1,0,0),(vec 0,1,0),(vec 0,0,1)) //poor man's matrix
   ...
>
Precendence:
   Literal values;
   Unary operators (+, -, !, ~)
   *, /, %
   +, -
   &, |, ^
   <<, >>
   == (=), !=, <, >, <=, =>
   &&, ||
>
No assignment or comma operators; assignment is a statement.
Precedence rules differ here from C.
>
Unlike a C style tokenizer, any combination of operator symbols will be parsed as a single operator, regardless of whether or not such an operator exists (this shaves a big chunk of code off the tokenizer logic).
>
For now, the language lacks any ability to define proper functions in- language, so the only functions that exist are built-in.
>
>
For the first time in a very long time, this interpreter has an "eval" command in the console. Though, one needs to use parenthesis to eval an expression as (unlike JS or similar) statements and expressions are different and one may not have an implicit expression in a statement context. For my first major script language (JS based), there was an eval. Howerver, with the design of my later BS2 language, eval was no longer viable.
>
>
Where, there is a split between design choices that make sense for a light-duty script language, and one meant for "serious work" (more features, better performance, etc). Sometimes, one might climb the ladder of the language being better for implementation tasks, while ignoring things that are useful for light-duty scripting tasks (trying to make a language that does both but maybe ultimately does neither task particularly well).
>
So, say, the fate that befell my original BGBScript language, was that the VM became increasingly heavyweight (more code, more complex, ...) and less well suited for implementation tasks (as it tried to take on work that might have otherwise been left to C). BGBScript2 had essentially turned into a Java like language, not as good at implementing stuff as "just write everything in C", yet no longer great for scripting either (namely, Java-style code structure is not particularly amendable to interactive use of "eval"; nor is a statically-typed language particularly amendable to "hot patching" live code, etc...).
>
Like, when a scripting VM expands to 300 or 500 kLOC, using it for scripting a project is no longer as attractive of an option. A partial fork of this VM still survives though, I just now call it "BGBCC" and am using it mostly as a C compiler for my custom ISA project.
>
Though, from what I can see, modern JavaScript seems headed down a similar path.
>
A similar issue seems common in many long lived script VM projects. They get faster and more powerful, all while loosing the properties that made them useful for their original use cases.
>
>
>
Granted, the other option is to effectively "roll the clock backwards", and revert a language to a simpler form.
>
Judging by the past, could probably do another JavaScript style VM in around 10k LOC or so. Maybe less if the design priority is keeping code small. Besides the block structuring, there are "gotcha" things like break/continue handling that one needs to deal with. Naive AST- walking interpreters don't deal well with non-local control transfers (like break/continue/goto).
>
So, say, if the minimum becomes:
   Parse language to AST;
   Flatten AST into some sort of linear IR;
   Interpret linear IR.
Then this would set a lower limit on the size of the interpreter.
>
Well, and giving up on 'break' and 'continue' wouldn't be great for usability. Then again, maybe there could be a "break/continue" flag, where the AST walker would simply walk outwards until getting back to a place where the break/continue status could be handled. Could maybe save a few kLOC.
>
Other design goal basically being to limit it to a similar feature set to ES3 (though, leaving out some ECMAScript's misfeatures).
>
>
>
Could do something Lisp-like, but I suspect the minimal interpreter for a Lisp dialect will still be larger than for an 80s style BASIC dialect.
>
It is different tradeoffs:
   + more expressive
   + less cruft
   - Actually using Lisp syntax sucks worse than BASIC
>
>
Or, smaller still, something like Forth or PostScript, but, the general experience of trying to write code in these languages is a lot worse than BASIC.
>
>
>
I had also half wanted a SCAD interpreter, but, FWIW, the syntax for SCAD is vaguely similar to that of JavaScript (and, the idea of doing SCAD style stuff in unstructured BASIC does seem a bit ugly).
>
But, then again, when I threw together this interpreter, had imagining using it for NPC event scripting (as opposed to CSG).
>
>
Where, say, SCAD code looks sorta like, say:
     color("black")
     {
         translate([-1,7,10-jawrot*0.08])
             cube([2,0.25,jawrot*0.1]);
         translate([-1.75,7.8,14])
             cube([0.75,0.25,0.75]);
         translate([1.0,7.8,14])
             cube([0.75,0.25,0.75]);
     }
     color("brown")
     {
         translate([-4,-1,14])
             cube([8,8,5]);
         translate([0,5,16])
             rotate([0,0,45])
                 cylinder(h=4,r1=5,r2=3,center=false,$fn=4);
     }
>
...
>
>
A moment of nostalgic feeling for me, reminds me of working on Finray - ray tracing interpreter. Never could get the CSG to work quite right.
 
I have done CSG before.
Albeit likely in a different form from what would be relevant to a raytracer.
One simple form of CSG was the "Brush" system used by Quake and friends for building maps:
   Start out with a group of N planes in a brush
   Generate a very large polygon for each plane
     Generally, the maximum size of the map
   Clip each polygon against each plane, discarding the outside parts.
   Clip every brush against every other brush.
     Discard any polygon that is entirely inside another brush.
   Use magic to determine if map is "sealed";
     If so, discard any polygons that are external.
       IIRC, face normal has line-of-sight to edge of world.
   Then, build the BSP tree and similar from the polygons, ...
My first 3D engine originally did something vaguely similar, except that the BSP was organized as a tree of brushes rather than polygons.
This allowed dynamically mutating the map, as adding/removing/resizing a brush could allow dynamically rebuilding any associated geometry.
I also had it able to import maps in several formats:
   Quake 1/2 format;
   Quake 3 format;
   Half-Life format;
   Doom 3 format.
For my own map attempts, I was mostly using the Half-Life format, as because (unlike id's maps), the HL format specified texture projection in a a way that "wasn't stupid". The optimal choice might have been a hybrid of the HL and Doom3 formats, say:
   ( px py pz pd ) texture [ sx sy sz sd ] [ tx ty  tz td ]
Where, px/py/pz/pd specifies the plane, and s* / t* the texture projection vectors.
As the Q1/Q2/Q3 and HL formats had given the plane as 3 X/Y/Z points (say, as a very large triangle), but you don't really need a 3 points to specify a plane.
Though, the Quake style maps lacked a standard "subtract" or "difference" operator, which was inconvenient. A workaround (borrowed from some Quake mapper tools) was to have a mechanism which specifies a brush as "negative"/"subtractor". Any other brushes which intersect with a negative brush are clipped against this brush (with new faces generated for the interior of the intersected regions).
The same basic stuff can be adapted to 3D modeling, just with a CSG operators giving finer controls. Though, does complicate the clipping process some (vs like in a Quake map) as now effectively one needs to walk a tree as you don't want to clip a polygon against a brush that has itself been clipped way, etc. And, similarly, unlike negative brushes, the effect of a "difference" operator will only apply to specific parts of the 3D model.
Well, and with typically higher level of abstraction, like you specify "sphere" or "cylinder" or "cube" or similar, rather than planes. Concept in this case that, internally, these just turn into brushes or similar.
I am not sure if this is exactly how OpenSCAD approaches it, but this is how my past code approaches it in any case.
At the end of the process, whatever polygons remain, are the 3D model.
I had some C code from before which had used CSG mostly to make solid parts for 3D printing.
Though, for this use-case, I had since mostly switched to OpenSCAD.
But, had copy/pasted some of this code to a subdirectory in my BT3 engine's codebase, though I am not inclined to have this as part of the main 3D engine binary (well, in theory, I haven't looked too closely at this part of the code recently to confirm its integrity or completeness; I was initially mostly just after the associated STL handling code).
But, my thinking here is that effectively the CSG / Brush-Tree code will be exposed via gluing syntax onto the BASIC interpreter.
Though, with an exposed interface more like SCAD and less like Quake "map brushes". Though, may be useful to offer something for semi-manual brush building as well, which is seemingly lacking in OpenSCAD.
Possibly also some differences, like primitives which can specify their full bounds and starting locations rather than needing to use "translate" quite so often, or a "rotate" where one can specify the reference origin rather than "translate() rotate() translate()".
There are some uncertainties, as my past code was more organized around "build the brushes in the places they are needed" (more like a Quake map) as opposed to translation and rotation trees.
Though, a possible option is that a translate or rotate operation will clone the brush-list and then apply the transform to all of the brushes destructively.
A non-destructive approach would be possible, but would require that now each clipping operation would need to deal with nested transforms. Probably could be done, likely would just involve projecting the planes by a matrix for things like clipping operations (and doing matrix multiplies or similar when walking up the tree).
Also would need to decide whether texturing should be treated as part of the brush primitive, or applied afterwards during the model generation process. The latter approach makes more sense if I intend to use it in a similar way to SCAD. But, then I have to decide whether it applies "outside inward" in the tree, or is applied destructively (as polygons move "up" the tree).
If using a non-destructive tree structure, it may make sense that the final projection of each brush (and its final texture and color) are already known by the time the CSG clipping starts. But, modifying polygons as they move up the tree makes sense as the CSG process itself inherently involves destructively modifying the polygons.
...

I usually incorporate TinyBasic (written in assembler) into the system once the CPU is running fairly well. I had at one point a grid of CPUs running TinyBasic and one could switch between them with a command like: CPU=10
It worked even from the command line. TinyBasic as an OS shell.
 
My basic was written in C, and this was actually the second BASIC interpreter I had written in this style. I went with this sort of BASIC since, as noted, the needed interpreter is small enough (and language design simple enough) that I can just pull something together out of thin air as needed.
Though, this would have been much less true of a more advanced language.
Like, for something like another JS interpreter, I would actually need to put effort into it.
Or, use my old BGBScript VM, but it is too big/involved, and I am not inclined to throw an interpreter at the problem that is around 10x the size of the program I would be adding it to.
Similar issue with Google V8, which was around a similar size range last I looked. My BGBScript2 VM was smaller, but the language is Java-like, so not an ideal fit for the intended use case.
And, would likely need to dig around in my code from around 2006 or similar to find a "not huge" version of my original VM (that also wasn't as horribly bad as the 2004 version, which was ironically, the direct ancestor of what became BGBCC).
Or, more likely, just throw something together now, with the goal of making it specifically for being "small with a cheap VM" and not "has a semi-transparent FFI and goes fast". Probably aiming for a "trimmed down and cleaned up" ES3 variant.
And, as noted, there was the other traditional option of Lisp variants, which can be split into two camps:
   Lexically scoped, like Scheme or Common Lisp
     Cleaner semantics, but requires a more complex interpreter.
   Dynamically-scoped simple AST walker.
     Like Emacs Lisp.
There was also GNU Guile, which implements a Scheme variant, and was partly what got me started writing interpreters in the first place:
Guile was seriously annoying me on some points;
I looked at the code, saw that it wasn't all that complicated, and proceeded to write my own version the way I wanted it.
Then ran into issues, one of which is that coding in Scheme actually kinda sucks. Next interpreter written: the first BGBScript.
Ironically, the 3D engine in question already has the core of BGBCC's AST code, so I theoretically "could" copy/paste the parser from BGBCC and maybe strip off a lot of the C related stuff and similar (and then maybe, once again, walk the XML trees as an interpreter).
But, no, I don't want to go that route.
The 2006 VM had used a different strategy:
It parsed the JS like code into a Scheme-like form internally;
It then ran said code on what was more-or-less a copy/paste of my prior Scheme interpreter.
This VM was mostly used from around 2006 to 2014, where it became a more powerful language, but the size of the VM expanded by several orders of magnitude (as it went to a statically-typed, type-inferred core, which internally compiled down to native x86-64 machine code).
Basically, the backend was vaguely similar stuff to what BGBCC does now, except that it would essentially link the code internal to the currently running program (and at the time, BGBCC was being used as a FFI generator, mining headers and using it to generate metadata that the VM could use to call back into C land).
So, it was using S-Expressions as the AST, which were in turn composed of "cons cells", each of which holds a pair of tagged references (traditionally known as "car" and "cdr"), which are organized into linked lists to build structures (car holding the current list item, cdr pointing to the next cons cell, until the final cdr usually holds a null, usually designated as "()" in Scheme).
Something like:
   function foo(x,y) { x+y }
Translating internally to something like:
   (define (foo x y) (+ x y))
By the time of the BGBScript2 VM, I had simplified things, and went over to a mechanism more similar to .NET's P/Invoke (not as transparent, but less complicated on the VM side).
But, as noted, I could no longer hot-patch code or eval things, which was a drawback.
But, as can be noted, the internal architecture of the BS2VM was also not too much different from BGBCC, just with an AST system represented in a more JSON-like form, rather than XML.
...

Date Sujet#  Auteur
24 Apr 25 * Misc (semi OT): Well, distractions...5BGB
25 Apr 25 `* Re: Misc (semi OT): Well, distractions...4MitchAlsup1
25 Apr 25  `* Re: Misc (semi OT): Well, distractions...3BGB
25 Apr 25   `* Re: Misc (semi OT): Well, distractions...2Robert Finch
25 Apr 25    `- Re: Misc (semi OT): Well, distractions...1BGB

Haut de la page

Les messages affichés proviennent d'usenet.

NewsPortal