Embedded Scripting -- Tcl? Lua? Thoughts? Suggestions?

As in the example I posted. You *think* of tuples as structs. But, they don't really exist in an abstract sense. I.e., they only exist when you are accessing them -- as (...).

E.g., you can have a function return a (string, int). Another similar function can also return a (string, int). You don't consider the results of these two functions to be "the same type" -- in the sense that (in C) you would create a typedef struct that *both* would use. You don't reference the "members" of a tuple by "names/identifiers". Rather, *positionally*, via a "(...) = " assignment.

One of the annoyances of Limbo is exactly that. You end up with code stanzas like: (name, count) := function_that_returns_a_string_and_an_int(...); ... (name, count) := function_that_returns_a_string_and_an_int(...); The second invocation screws you because you've already got a "name" and "count" in the current scope. So, you end up rewriting: (name, count) := function_that_returns_a_string_and_an_int(...); ... (name, count) = function_that_returns_a_string_and_an_int(...); or, declaring name and count "up top" somewhere and then: (name, count) = function_that_returns_a_string_and_an_int(...); ... (name, count) = function_that_returns_a_string_and_an_int(...);

Yes. The struct analogy in Limbo is the adt (Abstract Data Type) which is often a (user-)named type.

Modules (which are a formal type) are essentially adt's -- the data and function members thereof accessed through pointers to the underlying module: "module->member" notation (where "module" is a pointer to a loaded "Module")

Limbo avoids this by letting (forcing!) you to declare the variables into which the result(s) get stuffed. So, it's just syntactic sugar, of a sort.

I.e., in the first (":=") example, name and count are declared (created) in the current stack frame so there's a place for these "return values" to get stuffed. In the second ("=") example, the variables were previously declared (and accessible in this scope) so the same idea applies. There's no "hidden" allocation/GC required (beyond that of cleaning up the stack frame when the block exists).

Reply to
Don Y
Loading thread data ...

I don't know anything about it, but Micro Python is a thing that exists. It is a Python 3 implementation optimized for microcontrollers.


Reply to

It's too large for this purpose, apparently. I'm told it needs around


I've been looking at Microscheme and Picobit (both small Scheme implementations). Microscheme (intended for AVR8) is too minimalistic but Picobit is interesting. There is an ARM port of Picobit but I don't know how well maintained it is.

Reply to
Paul Rubin

Limbo supports this with the "nil" object. It;s required for parameters in tuples that are to be "ignored".

It's also "good practice" to use it in places where you *want* to ignore something (e.g., a return value) and yet indicate that you are deliberately ignoring it (vs. "you forgot about it").


nil = malloc(...)

Reply to
Don Y

Yes. Deliberately ignoring returns is fine.

My gripe is against allowing to ignore the returned value(s) completely ... which too many languages allow ... or to catch the first value (or first N values) of a multiple return and ignore the rest *without* any syntactic cues.

Lisp has both flaws - Scheme has only the first. In either language you can completely ignore all the returned values, but if you want the

3rd value of 5, in Lisp you need to catch at least the first 3 values; in Scheme you must catch all 5 values. Unfortunately neither Lisp nor Scheme allows reusing a name to bind multiple values from the same RHS expression, nor do they have explicit syntax to suppress binding of particular values. [Lisp's multiple-value-bind does implicit suppression of trailing values, however there is no warning if you don't catch everything. Scheme's let-values complains if the LHS doesn't match the RHS.]

I like certain aspects of multiple return/assignment and I included it in one of the languages I designed. Like Scheme, I required that every LHS identifier be matched by a RHS value, but I also included a LHS placeholder which throws away the corresponding RHS value: I used "_" because that what Prolog used for anonymous matching and I like the visual effect of a "hole" in the expression. YMMV.

I also let the RHS be a list of arbitrary expressions so long as it produces the correct number of values. So you could say stuff like:

a,_,c,d,e = func_returning_3(), func_returning_2(); and a,b,_,d,e = 1,2,func_returning_2(),(1+2);

It was not any more efficient than evaluating the expressions and performing the assignments in sequence, but I considered that choosing to use the multiple assignment form might be conveying something semantically important to the programmer ... and it was fairly easy to do once I had multiple return.


Reply to
George Neuner

Exactly. This is the same sort of thing as adding an explicit cast: "Yeah, I *know* this is an APPLE. But, it's safe for us to PRETEND it's an ORANGE, right now..."

Show the *next* developer that you were aware of something -- and took steps to deliberately ignore it -- instead of potentially leaving him wondering, "Why didn't he check the error code, here?"

The nature of lists -- they have no prescribed lengths! :-/


That's the nature of this subthread. Like being able to mark the source to say "ignore this warning in this place. And, if the warning DOESN'T appear, bring that to my attention!!"

I'm not fond of '_' in any use. Even seeing it in identifiers makes me cringe. It still remains part of my typedef names (e.g., foo_t) but I've been disciplining myself to get rid of it there, as well.

Limbo's "nil" (for placeholder) seems to stand out well enough (i.e., it doesn't bring up suggestions of some other identifier you might have encountered in your code).

It also has value (actually, there's a pun hidden in that!) in assignments:

comm_links: list of channel; ... comm_links = nil; // cut all lines!

A list is a list, right? :>

I'd be leary of a missing terminator causing two func() invocations (on successive lines) to be cat'd (cons'd) together to fill a single list. But, having/requiring number of values to equal number of variables backstops that. (Too early in the morning to wonder if there are any pathological cases where you could still get screwed by something like that...)

C off to new class so I may actually get time for *work*, today! (what a novel concept!) OTOH, I'm still stumped on some syntax for the IDL so maybe work isn't such a good idea, after all! :-/

Reply to
Don Y

I've never had to read - or listen to - code over the phone. 8-)

I do agree that symbols can be too cryptic, but I also don't much like the Lisp way of spelling-everything-out-as-verbosely-as-possible. Lengthy names (particularly those trying to redundantly encode type) become unwieldy very quickly. There has to be some acceptable middle ground.

Oddly enough, I once used a Lisp variant which had both null and nil. null was nil ... I forget what nil was for. Too long ago.

Cards? Our machine had rows of those little "rice grain" toggle switches. We had to (re)enter the program in binary, one byte at a time, whenever the machine was reset. 8-)


Reply to
George Neuner

You have vision. How would a blind user "see" the code? How would I tell him what to write to solve one of his problems?

[I've fed various sources into several different speech synthesizers, haptic interfaces, etc. It is only then that you truly realize how much our programming languages have come to rely on "symbols"]

Or, do you just exclude them from that technology? :> One of the driving motivations for my current project is accessibility -- in more than just a "token" sense.

As I said, you can adopt *conventions* to make what you see more readily conveyed in (e.g., spoken) conversation. E.g., I'd never say "colon colon equals" when reading an Algol fragment to someone...

Hence the appeal of lots of specific namespaces, short programs/modules, avoiding reuse of an identifier in a nested scope, etc.

I only saw front panels on small minis. Any of the "bigger machines" (that were shared, of necessity) were pretty much "maintained". On the odd occasion, you might have to load something off tape (DECtape) but, for the most part, the system was "up" and most jobs were just submitted on decks of cards. You did your "editing" at a keypunch terminal in another room (or, a *single* terminal set up for "one off" quicky changes (like fixing a JCL card in case of an immediate abend).

It was a *delight* to advance to the point of being able to use an ASR33 w/103 modem to *type* in code! And, having a "local copy" on PPT... :-/

[I still have an ASR33]

The Reading Machine had a "control panel". Pull the front (cosmetic) panel off to expose the front panel of the minicomputer. Though, the switches were sizeable -- like 1/2" wide plastic paddles. But, 16 of them ("data") grouped in octal triads. Set address. Load data. repeat. Bit-switch in your diagnostic, a loader, etc.

And, count on the little ferrite cores to "remember" for you!

[Our first *DRAM* machine was disturbing! "You mean it *forgets* when it powers off??"]
Reply to
Don Y

I think what you are describing is Forth. There are two commercial vendors, MPE and Forth Inc. I know the MPE license allows you to embed the "interpreter" into your product for the customer to use. There are also open source Forths around, but with varying levels of embeddability and support. One is Mecrisp which is available for a few of the many ARM processors around and the TMS430 I believe. There are many others.

Forth can be idiot simple to use. The assumption is it has a serial terminal for I/O or something much like it. I/O can be redirected from/to files or anything else by using deferred words. It can run on the bare metal or under an OS. You can give as much power to your customer as you think they can handle or as little as you think is wise.

I've cross posted this to the forth group to get input from some of the regulars there.

Oh, about the "file system". If you are happy with no file system at all, Forth can treat your storage as "blocks" of 1 KB and no real files at all. You can't get much simpler than that.


Reply to

There are a number of Forth systems out there that fit the bill, including ours. A basic multi-tasking Forth system with SD card driver, FAT file system and file API layer can be done in around

32 kb of Flash on a Cortex-Mx class CPU.

Note that our Forth systems are proprietary, not FOSS.

Our website and contact details are below.


Stephen Pelc, stephenXXX@mpeforth.com 
MicroProcessor Engineering Ltd - More Real, Less Time 
 Click to see the full signature
Reply to
Stephen Pelc

I've advocated using Victor Borge's approach. But alas, I seem not to be taken seriously...

Reply to
Robert Wessel

Sometimes don't you wish Usenet had a "Like" button?

MPE's VxfForth is probably best for most of the platforms you are likely to be dealing with these days. I do recall suggesting Forth earlier in this thread. You really can design the script language you want and make it seem quite natural to the user.

Paul E. Bennett IEng MIET..... 
 Click to see the full signature
Reply to
Paul E Bennett

Not sure I follow. Wasn't he the wonky pianist/comic?

Reply to
Don Y

Yes, one of the things he was famous for was a routine in which he'd read something, and pronounce all the punctuation.

formatting link

Reply to
Robert Wessel

Ah! I'd only heard him playing piano with sheet music upside down, wearing a seat belt to the piano bench, etc.

A programmer can exercise some control/discipline over his/her choice of identifiers *if* non-grapheme output is a goal. But, the *language* tends to give very little flexibility in how and where symbols are used. Do you really need (want) to hear "open paren" "closed paren" for every conditional? loop? etc.?

You can create "modes" whereby special types of "displays" are interpreted according to a different set of rules (e.g., read "==" as "is equal to" and "=" as "gets", etc.). But, the more of these you encounter as a matter of course in a given bit of "input", the more taxing it gets to become.

Nonsense example (unchecked for correctness/syntax -- but, everything over which the developer has control has been chosen to be "pronounceable"; all other "words" are defined by the language):

implement fudge; include "sys.m"; sys: Sys;

SHORT, MEDIUM, LONG: con iota; FOREVER: con 1;

text: list of string; pipe: chan of (string, int);

init(args: list of string): int { argv = tl args; argc = len argv;

sys = load Sys Sys->PATH;

spawn consumer(pipe);

while (argc > 0) { line := hd argv; argv = tl argv; argc--;

size := LONG; // assumption else otherwise if (line == nil) { break; } else if (len line < 10) { size = SHORT; } else if (len line < 40) { size = MEDIUM; }

pipe mediums++; LONG => LONGS++; * => /* can't happen */ exit; }

sys->print("Received '%s'\n", input); } }

Reply to
Don Y

I wouldn't expect a blind user - or ANY user for that matter - to be reading code. At most I would expect to be read some error messages.

*Those* should be able to be pronounced by a screen reader.

No. I expect blind and deaf to be abe to use things, but I don't expect any non-technical person (~95% of the population) to be programming them.


Reply to
George Neuner

Hi Tim,

I am the author of Mecrisp-Stellaris, a Forth implementation that compiles to native code on the target and needs 16 kb Flash and about 1.5 kb RAM on an ARM Cortex M0/M3/M4 for its core when used standalone. RAM is necessary mostly for input buffers and its two stacks. As you called for Open Source, I think you are fine with GPL3 licencing ? If you are, then I am there for your help. Until now, Mecrisp-Stellaris is designed to be monolithic, but you can change initialisation code, memory map and interrupt handling for your needs. Something you still need to think of is wiring the parts of your code which should be user available into the dictionary and do parameters-on-stack interfaces for them.

If you like to try, Mecrisp-Stellaris already runs on chips from TI, STM, Freescale and NXP. An emulator for a Freescale M0 target running on Linux is included.

Best wishes from Germany, Matthias Koch mecrisp.sourceforge.net

Reply to
Matthias Koch

So, they should *hire* folks to customize things to their needs?

bedtime() { if (stove on) warn("You left the stove on!") if (door unlocked) warn("Don't forget to lock the door!") if (garage open) warn("Don't forget to close the garage!") if (lights on) { warn("There are still some lights unexpectedly left on. "\ "Should I shut them off?") if (yes) lights(off) } heat(bedtime) }

I think that is changing. Things are getting too complex for a "one size fits all" approach to all users.

"Non-programmers" already write macros in Excel, Word, etc. And, even write VB scripts "without formal training". I don't think it a stretch to assume they'll be able to "code" these sorts of things -- esp if there are very high level abstractions at their disposal.

Reply to
Don Y

No. For the vast overwhelming majority of users, customization should

*not* require manually writing (or even editing) a script. See below.

I didn't say anything about "one size fits all". I said the vast majority of people should not be programming - an understatement to be sure.

The best way I have seen has been "menu" [includes icons] driven UIs that let people select functions and point out data to operate on, and then generate the script. They can't guarantee the script will do what the user wants, but they can guarantee that it is free from stupid errors. Combined with operations that are intrinsically safe, this is a good way to allow user customization.

It is, however, more heavyweight than a telnet session with a line editor.

Actually, the ability to write a Word or Excel macro says absolutely nothing about the ability to write, e.g., a correct shell or Python (or whatever) script. When a macro barfs, you can reload the file and try again. When a script barfs, it may destroy the data it was manipulating. Every day businesses suffer lost data and lost productive hours due to amateurs writing "programs".

*Your* scripting language may be intrinsically safe, but that doesn't invalidate my point.


Reply to
George Neuner

That only makes sense for "trivial" systems where the only things a user would want to change are "drop in" items (e.g., a ring-tone on a phone).

When you want to program *behaviors*, you can't expect the way one person (or even one *class*/group of people) to behave to bear any relationship to some other group.

Unless you try to build a "be all" system with umpteen "configuration options" that you grill the user about.

wakeup() { #if USER_DRINKS_COFFEE power_up(coffeepot); #endif

#if USER_DISLIKES_BRIGHT_LIGHT_ON_AWAKENING lights(kitchen, dim); #else lights(kitchen, bright); #endif

#if USER_LISTENS_TO_NEWS_WHILE_SHAVING audio(bathroom, radio(news_channel)); #endif

#if USER_SHOWERS_IN_MORNING hot_water_heater(on); #endif


I don't think that's the case, anymore.

How many appliances require some sort of "customization"? How does a blind user configure his wireless router? Firewall? "Program" his TV/cable box/DVR/XM radio? (do they even *have* provisions to present data in a non-visual form?)

We were looking at refrigerators the other night. How would I know the current temperature setting -- *buy* a talking thermometer and leave it in the frig for a while?? How would I know if the "extra compartment" was currently configured as a freezer, refrigerator or "soft freezer"?

Everything is getting more "customizable". And those customizations are becoming more *dynamic* in nature. It's not just a "set and forget" sort of thing.

Yes, that's how my *interactive* user interface works. Because menus can be easily presented in various output modalities (speech, haptic, visual, etc.).

Of course! But, PREVENTING them from writing a script -- or, typing in a script that is read to them over the phone by one of their friends/colleagues/etc. -- isn't a solution. And, relying on a "language" that is prone to casual errors (e.g., "==" vs. "=" bites "experienced programmers"!) just makes the problem worse.

When you discuss a piece of code with a colleague, do you pronounce every punctuation mark? Chances are, you adapt your presentation (discussion) style to your expectations of the other party's capabilities.

Limbo, for example, forces me to distinguish between ':', ':=' and '=' far too often. Even when the person I'm talking too can *see* what I've just described ("Hmmm... where did you *declare* x? I guess this 'x =' should really have been an 'x :=')

In the past, I would often "proof" PCB layouts against schematics with colleagues (in the days when we did this sort of stuff by hand -- with tape and rubilyth). One of us would trace signals on the schematic while the other traced foils on the layout. If I was confident in the abilities of my "counterpart", I'd say something like: "D1 to C5 to J1 pin 3" for a particular signal/foil path. If I *wasn't* confident in their abilities, I'd be far more explicit: "cathode of D1 to anode of C5 and on to pin 3 of J1"

When you're dealing with a "casual user", you have to be more explicit -- OR, use a language that doesn't introduce "unspoken" items into the dialog (e.g., punctuation, whitespace, etc.).

Sure! But that's usually because they don't have the ability (and inclination) to easily and thoroughly *test* what they've written! Or, do so in a "safe" manner. And, is compounded by folks trying to do too much *in* a "program" (contrast this with my "bedtime()" script).

The whole move towards the "safer" programming environments (and those are really only intended for "REAL" programmers) indicates that programs can't continue to be intricate, error prone creations. The system designer/implementer(s) have to consider the abstractions that they present to the "user" (programmer?) to maximize the chances of the user "getting it right".

E.g., I don't expect folks to write SQL to extract the information they require from my DBMS. Rather, I provide a more abstract interface that focuses on what the user (of that particular dataset) is likely to want to do to/with that data.

answer_phone() { result = lookup(phone_book, calling_number); case result { "friend" => reply("Don's not home, right now. Leave a message.") "employer" => reply("Don's on the can! Could he call you back?") "SO" => reply("I'm on my way! See you soon!") "police" => reply("No habla ingles") * => play(Disconnected_Pipe_tones) voice(Generic_Telephone_Operator_Lady) reply("We're sorry. The number you have dialed is no longer in service. Please check the number and dial again." log(phone_log, calling_number, time) } }

By way of example, there should be no need to specify a "time" argument in the log() invocation; it should automatically stamp each message with the time the function was invoked (as that's *probably* of interest to anyone using the facility).

Note that this code fragment has lots hiding *behind* it. But, none of that stuff should be of concern to the person writing it! Nor is it an unusual desire for a typical user (though his choices of categories and associated actions may vary).

It's no different than someone posting a message here (or in a forum) asking "how do I configure my FOOMATIC2000?" Or, "How do I fix this problem with that piece of equipment?" etc. You don't have to (necessarily) *know* how the code works -- and certainly don't have to know how to write it "from scratch" (many "programmers" can't work from a blank sheet of paper!). But, you should be able to "type it in" without stumbling over "invisible" symbols/punctuation...

Reply to
Don Y

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.