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!"
If you don't know what happened
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
_callstack
Level 1 <>
_callstack
If your program has stopped, and debug compiling has been enabled Print the last state of the callstack. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━example
Program Structure
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━head
Level 1 <>head
Initialize the Lowerdash Compiler ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━_debug
Level 1 <>_debug
Enable debug compiling for imports after this point Allows using _callstack ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━IMPORT
Level 1 <>IMPORT FILE$
Import a file. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━hend
Level 1 <>hend
Compiles the temporary file and makes it executable ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━compile
Level 1 <>compile FILE$
Compile to the file system. You can load this in place of Lowerdash to skip compilation ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━compile_lib
Level 1 <>compile_lib FILE$
Compile to the file system as a Lowerdash library; this file does not include Lowerdash ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━main
Level 1 <>main
Execute module code and initialize Lowerdash runtime. Program execution should not return above this point. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Data Management
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━DICT
Level 2 <>[COMMON] DICT myDict
Create a callable function myDict(KEY$)[icode ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ A dictionary is a table of strings, each pointing to their own unique value. DICT is immutable. See MDICT for a dictionary you can update. DICT is built at compile time and each key can only point to a static statement. Its useful for namespacing singleton members or exporting configurationexample
DICT myDict KEY-> "RESULT" END
PRINT myDict.KEY*Dictionaries return Nil$ when a key is not found. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TUPLE
Level 2 <>KEY -> {STATEMENT}
Add a tuple to a DICT or MODULE definition ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ PROTIP: A tuple can return a function pointer!examples
ANS->42
SUB->"mySubModule"
WOAH->(privateVar + privateArr[0])━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[]
Level 2 <>VAR arr[] = [{literal list}]
Assign a list of literal values to a new array. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━example
VAR myArr$[] = ["dog", "cat", "bird"]━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FOR
Level 2 <>FOR {ITEM} IN {ARRAY} [INDEX {I} [STEP STEP%]] ... NEXT
Iterate through an array, setting ITEM equal to each value ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━example
FOR S$ IN myStrings$ PRINT S$ NEXT━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
MODULE
Level 2 <>MODULE MyMod [PROTO parentModule]
Sets the current module, creating a new "namespace". Creates a callable function MyMod(KEY$) to lookup properties in the module's namespace ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━VAR
Level 2 <>VAR myVar[,myVar...]
Declare a variable in the current modules namespace. Also generates a setter function MyMod__setMyVar V ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ This will set an entire array referenceexample
MODULE MyMod ... VAR myVar VAR myArr[0]Use in MODULEs
PRINT me.myVar me.myVar = 42 PRINT MyMod_myVarme.myVar will be desugared to to include the current MODULE name at compile time if myVar exists at the module level. Use Everywhere
PRINT MyMod("myVar") CALL MyMod("setMyVar"), 5 PRINT _("MyMod","myVar")━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
STATIC
Level 2 <>STATIC DEF myStatic [args...]
Define a callable function in the current MODULE's namespace ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ STATIC DEFs can be accessed through the module, or any of its instances:CALL MyMod("myStatic") CALL MyMod_myStatic myO$.myStatic
example
MODULE Math STATIC DEF add(A, B) RETURN A+B END
PRINT Math_add(1, 2)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@LABEL
Level 2 <>@@LABEL
Define a label in slot 3 under the current MODULE's namespace Should end with RETURN@@LABEL
Shortcut for labels that belong to the current MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@LABELs can be accessed by name onlyexample
MODULE Hello @@myLabel PRINT "Howdy!" GOTO @@myOtherLabel RETURN @@myOtherLabel PRINT "Ho!" RETURN
GOSUB "3:@Hello_myLabel"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PROTO
Level 2 <>PROTO(K$)
Lookup a property on the PROTO of the current ME. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━example
DO proto.new━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CONSTRUCTOR
Level 2 <>EXPORT DEF new([args...])
Define a constructor for the current module ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ When a constructor is defined on a module, Instances of that module can be instantiated using new. It shouldreturn meor another pointer to store for reuse. #TODO BETTER WAY?
example
MODULE Hello ... EXPORT DEF new() RETURN me END━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
new
Level 2 <>new <Module>([args...])
Create a new instance of a module and return a pointer. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━example
VAR myO$ = new MyMod([args...])━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EXPORT
Level 2 <>EXPORT DEF myMethod [args...]
Create a callable function under the current module that operates on a dynamic instance ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Export DEFs can be defined after a MODULE's VARs and MEMs just like STATIC DEFs An EXPORT DEF is namespaced under the current module, and always operates on an instance. Calling an EXPORT DEF from a Module will cause an error. EXPORT DEFs get access to ME references.example
MODULE Hello ... EXPORT DEF greeting PRINT "Hello, " + me.who$ + "!" ENDEXPORT'd DEFs are available on instances of a module.
myO$.greeting━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
MEM
Level 2 <>MEM myMem%[,myMem...]
MEM myMem#[,myMem...]
MEM myMem$[,myMem...]
Define a member that will be allocated when new is used Generates an export DEF _setMyMem ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Heap members must be typed Calling new on a module will allocate memory for each member ( and any members on the prototype chain ) and create setters under the namespace of the created instance. Members are not available on the module itself.example
MODULE Hello ... MEM who$ ...━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ME
Level 2 <>ME(KEY$)
Lookup members on the current instance if there is one, as well as VARs and STATIC DEFs on the current moduleME("")
Return a reference to the current instance ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━examples
VAR V# = me.myDouble#
CALL me("maybe")
me.maybe
me.myString$ = "Value"me is bound to the MODULE in STATIC DEFs and will use a Static Dispatch at compile time rather than runtime. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
TYPE
Level 2 <>TYPE(KEY$)
Lookup static members on the current instance if there is oneTYPE("")
Return a reference to the current instance's type ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━examples
VAR items$ = type.items$protip:Can be used to dynamically dispatch from a prototype! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
_DEL
Level 1 <>_DEL O$
Delete the passed reference. Deallocates all memory and destroys the reference. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━example
EXPORT DEF delete _del me END
Level 1 Syntax
To access members, methods, statics, and constants on any object use the lower dash!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━_
Level 1 <>_(Mod$,Key$)
Lookup a property on any object Level 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Use Level 2 syntax when available!examples
PRINT _(O$,"key")
CALL _(O$,"method"), arg1,arg2,...When looking up STATIC and EXPORT DEFs, they'll be returned as strings to be used with the CALL command. EXPORT DEFs strings should be used immediately to avoid a stale "pointer".
To access module VARs and STATIC DEFs directly, without a lookup cost
PRINT MyModule_myStatic
MyModule_myStaticDef "msg"
To create an instance of a module with a public constructor use new
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━new
Level 1 <>new(Mod$)[, args...]
Create a new instance of a module, and return its constructor function ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ YOU SHOULD CALL THE CONSTRUCTOR IMMEDIATELYexample
VAR myO$ = CALL(new("MyMod")[, args...])
Hello World!
_HELLO
MODULE Hello MEM who$ EXPORT DEF new() return me END EXPORT DEF greet PRINT "Hello, " + me.who$ + "!" END STATIC DEF run VAR greeter$ = new Hello() greeter$.setWho$("World") greeter$.greet END
HELLO_WORLD
EXEC "PRG2:LDC" head ━━━━ import "_HELLO" ━━━━ hend main ━━━━ Hello_run
Objects! (Using Level 1 Syntax)
BALLS
LOAD "PRG2:LOWERDASH_C",0: USE 2 head ━━━ import "_SCREEN" import "_BALL" hend ACLS main ━━━━ VAR Bs$[10] FOR i=0 to 9 VAR b$ = CALL(new("Ball")) PUSH Bs$, b$ NEXT WHILE TRUE GCLS FOR i=0 to 9 CALL _(Bs$[i],"update") '* CALL _(Bs$[i],"draw") '* NEXT WEND
_BALL
MODULE BALL PROTO INBOUNDS ━━━━━━━━━━━━━━━━━━━━ VAR R% = 20 MEM C#,DX#,DY# EXPORT DEF new() me.X# = Screen("W")/2 me.Y# = Screen("H")/(RND(5)+1) me.C# = (&HFF<<24)+RND(&H1000000) 'Make all the balls smaller DEC Ball_R% return me END EXPORT DEF draw GCIRCLE me.X#, me.Y#, me.R%, me.C# END EXPORT DEF update 'Move the ball me.X# = me.X# + me.DX# me.Y# = me.X# + me.DX# 'Check if it should bounce if me.clamp("X#", "W") THEN me.DX#=-me.DX# if me.clamp("Y#", "H") THEN me.DY#=-me.DY# END
_SCREEN
COMMON DICT SCREEN W->400 H->240 D->1024 END MODULE INBOUNDS ━━━━━━━━━━━━━ MEM X# MEM Y# ' Keep something in screen and let the caller know it tried to leave. ' could be a ball or a bullet DEF clamp(V$,D$) IF me(V$) < 0 THEN me(V$) = 0 RETURN TRUE ELSEIF me(V$) > Screen(D$) me(V$) = Screen(D$) RETURN TRUE ENDIF RETURN FALSE END* _(Bs$[i],...) will cause a context switch. Two of them per entity per loop is expensive. Use sprites and IDs in groups. Lowerdash includes a set of definitions used to do the parsing and hacking. All of these functions are COMMON DEF'd in the standard Lowerdash main file.
Data
EAT VIgnore a value.
isNil$(V)Check if a value is equivalent to Nil$.
isObj$(V)Check if a value is an object reference
typeOf(V)Checks the type of a value 0 Double 1 Int 2 String
rando(MAX%)A random with a random seed. Double your entropy!
aDel aEmpties an array regardless of type.
Strings
trim_r$(S$)Remove trailing character. Might be a line break, who knows!
trim_s$(S%)Remove white spaaaaaace from the beginning and end of the string
drop$(S$,N%)Remove the first N% characters of S$
splitAt$ S$,I% OUT L$, R$Split S$ at I%
split$ S$,K$ OUT L$, R$Split S$ at the first occurance of K$
splitI$ S$,K$ OUT L$, R$Split S$ while ignoring case
splitA$ S$,K$ OUT L$, R$Split S$ after K$
splitB$ S$,K$ OUT L$, R$Split S$ before K$
startsWith$(S$,K$);)
endsWith$(S$,K$);]
contains$($S,K$)K$ is in S$ at least once
containsw$($S,K$)K$ is separated by spaces. tokenize a string.
allcaps$(S$)Upcases any lowercase letters in S$ From ColeslawProductions