I appreciate that you take the time to make a documentation. It's good to see that the static method could be called with [module]_[method] instead of using the CALL command.
I have some questions about the array how the VAR syntax:
How the setter generated for arrays work? It assign the entire array or an index of the array?. Also, could we override the setters.
I ask this because I'm considering to use lowerdash instead of SB3CC just for the namespace but I have modules that are like a POJO and I'm worried if I need to make too many changes on the name of the functions.
1 | Intro | | Performance |
2 | Structure | | Imports | | Modules | | Dynamic Objects |
3 | Examples | | Standard Library |
4 | Notes | | Roadmap | | Limitations | | Version History | | Comments |
9/15/16 - Added 0.7 features. Moved some commands to a separate list to write more text.
Update History
3/20/16 - Added 0.6.5 features
2/28/16 - Added benchmarks for New 3DS
2/10/16 - Ballerific changes
Update History
3/20/16 - Added 0.6.5 features 2/28/16 - Added benchmarks for New 3DS 2/10/16 - Ballerific changesTo use Lowerdash initially, load "LDC" into PRG2.
EXEC "PRG2:LDC"
Lowerdash provides "syntactic sugar" for SmileBasic. The Lowerdash language is a superset of the commands available in SmileBasic, and when compiled, the new commands and data structures are transformed into correct SmileBasic code.
Lowerdash is designed to follow modern practices, enabling you split up your code into easy-to-understand bite-sized chunks with descriptive names. Object-oriented encapsulation means you can re-use variable and function names and not end up with a soup of BX,CX,DX,EX,FX,... and the mental gymnastic that come with keeping those names straight.
What makes Lowerdash different, is that it does not attempt to emulate an entire runtime environment in an interpreted language.
Lowerdash takes advantage of the fact that commands that came with the language (built-ins) are compiled code that runs much faster. For example, VAR and GOTO both conform to the interface of a dictionary and both provide O(1) access time. Lowerdash code is compiled to plain old SmileBasic, and the runtime tries to step out of the way.
- Immutable Dictionaries are a common DEF using built-ins to be extremely fast.
- Modules are dictionaries with an extra check for available DEFs
- Lowerdash only has to search for properties on objects created using new. You can write code that is 1:1 performance with Vanilla SB if you do not use dynamic objects.
- Using new to create an instance will generate a string. When evaluated this string is parsed and takes at most 3 levels of recursion to return.
Benchmarks from New 3DS:
(times are averaged over 3 runs of 5000 iterations) *Dynamic Object Context Switch - 0.004 frames | 63ns *Dynamic Object Member Lookup - 0.003 frames | 53ns *Static Module Member Lookup - 0.001 frames | 3ns *Referenced Module Member Lookup - 0.002 frames | 23ns *Static Module Member By Name - 0.001 frames | 1ns This means that <200 lookups or context switches a frame will run at 60fps. Assuming each object makes an average of 5 updates to its data a frame: *60fps - ~30 objects / frame *30fps - ~70 objects / frame The Lowerdash runtime is loaded automatically into PRG3 by the compiler. The runtime stores the temporary program code in its slot and stores object instance related data. Once compiled, a Lowerdash program can be loaded into PRG3 instead of going through the compilation process. Your main program should live in PRG0 and be structured into 2 sections: Compilation and Main. This main program only has access to Syntax Level 1. At the time MAIN is called, LDC is unloaded, and PRG2 is freeCompilation ━━━━━━━━━━━━━━━━━━━━
Compilation is started by the [icode]head[/icode] command. The head of your main program should contain a list of [b]import[/b] statements - these are the files to be compiled. After the list of imports, the head block must be closed with either hend or compile The head block can contain compiler options like _debug to add features such as the ability to run a stack trace.example
head _debug import "_MyModule" import "RayCast.LIB" hend
Main ━━━━━━━━━━━━━━━━━━━━
Main is started by the main command In the main section, a simplified set of syntax is available as SmileBasic functions; Collectively this syntax is called Level 1 syntax. All standard library function are COMMON DEFs. Files are only imported once and then cached by Lowerdash. Normal files are parsed and desugared; Importing a file that ends in .LIB causes Lowerdash to treat it as a library. Libraries will not be processed and can be any code that does not need to be compiled. Imports can contain MODULE definitions, DICT definitions, IMPORTs, DEFs, VARs, and code to run at the start of the program. Standard DEFs and VARs are not namespaced, and can cause duplicate name errors if used in multiple files.Modules
An import can contain any number of MODULE definitions. A module definition starts with the MODULE command. You can end a MODULEDefinition with END or omit it - Any module definition implicitly ends at the next module definition, or the end of the file. MODULE can be immediately be followed by PROTO to provide a prototype for inheritance. The new module will inherit the functions and members belonging to the prototype (including the prototype of the prototype). VARs and MEMs can be declared after MODULE but before any DEFs to namespace them under the current module. Module related DEFs, STATIC DEFs and EXPORT DEFs, can be declared after VARs and MEMs. MODULEs can also be used as object-oriented classes if they contain a constructor functionexample
MODULE MyMod PROTO MyProto VAR sharedVar = 42 MEM instanceValue$ = "Default Value" STATIC DEF sharedPrint PRINT me.sharedVar END EXPORT DEF new(customValue$) me.instanceValue$ = customValue$ return me END END
Scoping in Modules
While working with object-oriented code, the variable names available to you will be changing depending on where in the program you are working. The variables currently "visible" and useable are considered to be "in scope". Scopes are recursive, if a variable isn't inside of a DEF SB will look in the Global Scope. When outside of a DEF: the scope will always be the Global Scope. This is where any standard DEFs and VARs live. When inside a STATIC DEF: ME is bound to the current MODULE. ME Lookups generate statically dispatched code referencing the MODULE directly. TYPE will dynamically dispatch to the current type of the first object instance on the stack. When inside an EXPORT DEF: ME is bound to the current object instance. ME Lookups will generate code that is dynamically dispatched at runtime. TYPE will dynamically dispatch to the current type of the first object instance on the stack.example
VAR myVar$ = "outer" MODULE MyMod PROTO MyProto VAR outer = 2 MEM instance% = 3 STATIC DEF sharedPrint VAR myVar$ = "inner" PRINT myVar$ # prints "inner" PRINT outer # not defined, prints 0 PRINT me.outer # prints 2 PRINT type.outer # could print any value set in a sub-module of MyMod. END EXPORT DEF new() return me END EXPORT DEF instancePrint PRINT me.instance% # prints 3 PRINT instance% # not defined, prints 0 END END
The State of Scoping
Right now theres a bit of ambiguity as to whether your lookup will respect dynamic dispatch. ME uses both types. Languages such as Java solve this issue with something called Lexical Scoping. For example:class MyClass { List[Int] myList = new ArrayList<Int>(); public void doSomething() { System.out.println(myList); } }myList will reference the correct ArrayList, because doSomething is nested inside MyClass. In the future, I would love to add Lexical Scoping and deprecate the current workings of ME and TYPE. ME would always serve to do dynamic dispatch expecting a heap object. TYPE could be used for dynamic dispatch directly to the Module, skipping pointer dereferencing. And MODULE variables could be statically dispatched if found where it makes lexical sense without a lookup. The problem is, keeping track of these scopes requires either a linear search for every ID (slow down compilation by a significant factor) or an advanced data structure (need to do 2-pass compilation, or maintain a secondary temp file). This area is one of the focuses for Lowerdash 0.8.
Basics
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━DO
Level 2 <>DO F$[,args...]
Call a function that would normally return something ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━To let people know you made a whoopsy, use ERR
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ERR
Level 1 <>ERR MSG$
Print the message, stack state, and memory Stop execution ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━example
ERR "Oh god! I am not good at petit computer!"