aboutsummaryrefslogtreecommitdiff
path: root/README
blob: 317e8f6e9c4f2d38be9993a01b431fbdd03b7315 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
╒═════════════════════════════════════════════════════════════════════════════╕
│ THIS PROJECT IS NEW and the code is UNFINISHED. DON'T USE IT                │
╘═════════════════════════════════════════════════════════════════════════════╛

LittleScript is a BASIC-like lightweight scripting language for
microcontrollers. It's meant for adding scriptability to your firmware, not
for writing your entire code in. LS has some restrictions to make it more
embedded-friendly:

- Identifiers capped at 6 characters
- Integer arithmetic by default
- No garbage collection (strings work like C strings)
- Allocation from a pool of fixed blocks
- Code is not stored all at once in RAM - can execute in place from slow
  external devices with some caching, etc.

┌─────────────────┐
│ Copying and use │
└─────────────────┘

This software disclaims copyright. Do what you want with it. Be gay, do crime.
Originally written by Alexis Lockwood in 2021. Ⓐ

┌───────────────────────────┐
│ What's implemented so far │
└───────────────────────────┘

- Basic high-level API
- Main lex-parse loop
- Expression evaluator
- Variable storage
- LET, PRINT, GOTO, GOSUB, RETURN keywords

┌───────────┐
│ Todo list │
└───────────┘

- Other keywords
- Non-integer variable types
  - String will always be supported
  - Floating point as optional compile-in
- Better testing
- Compile-time options for:
  - Internal asserts for better LS_INTERNAL_ERROR info
  - Hardwiring the ls object to a static, this should reduce code size a lot
  - Disabling features not needed, including
    - Hooks
    - Label cache
    - Fetcher (always execute in RAM)
- OPER_* should be LS_OPER_*
- Get rid of all this _ctx_ shit, it's a weird habit, they're just objects
- Use underscore prefix in ls_t for non user-controlled fields

╒════════════════════════╕
│ THE SCRIPTING LANGUAGE │
╘════════════════════════╛

┌───────────┐
│ Positions │
└───────────┘

Unlike BASIC which uses line numbers, LittleScript uses a system of named
labels and numbered small directional jumps. These are inspired by GNU
Assembler jump targets:

	REM examples/positions.bas
	GOSUB hello
	PRINT "Returned"
	END

	hello:	PRINT "Hello, world"
		GOTO +1
		PRINT "This line is skipped"
	1:	RETURN

There is no requirement of declaration order. If the target of a GOSUB, GOTO,
or any other line number argument is an identifier that has not been seen yet,
the interpreter will scan forward until it finds it.

When a number is used, it must be prefixed with + or -, and refers to the next
line forward or backward matching that number.


┌───────┐
│ Types │
└───────┘

LittleScript currently has no floating or fixed point support; all numbers are
integers and this is the default type. As with most BASIC variants, the $
suffix can be used for string variables:

	S$ = "Hello, world"
	PRINT S$

Strings are interpreted as ASCII and can contain all byte values including 0.
This allows them to be used to contain arbitrary binary data. Internally,
they are represented as a list of eight-character chunks.

Lists are single-dimensional only, and are implemented using linked lists
rather than arrays. No DIM is necessary to set them up since new elements can
always be linked in from the object pool, but DIM may still be used to fail
early if memory is limited. To optimize sequential access, a pointer to the
last element accessed is stored in the array object, thus re-accessing the
last element or the next element is O(1) and iterating over the sequence is
O(n).


┌─────────────┐
│ Identifiers │
└─────────────┘

Identifiers are eight characters long (same as a single chunk of a string).
An identifier must be created (via LET or another assignment keyword) before
their values can be accessed - they have no default. With no suffix an
identifier has integer type. With a $ suffix, it is a string, and with a
() suffix, it is a list.


┌───────────┐
│ Minimizer │
└───────────┘

Because small embedded arguments are the target, a minimizer is provided that
condenses a script to a smaller form. In the smallest setting, keywords are
replaced by single-byte abbreviations - these are listed in the keyword
documentation.


┌───────────┐
│ Functions │
└───────────┘

Littlescript supports true functions as an extention to the GOSUB syntax.
Arguments are given in parentheses in key=value format, and a new scope is
created in the called function with these values. The AS keyword can create
a return value:

	REM examples/add.bas
	a = 2
	b = 3
	GOSUB Add(a = a, b = b) AS c
	PRINT "a + b = ", c
	END

	Add: 'Add(a, b) => a + b
		RETURN a + b

Because a new scope is created, values are passed by value: if the function
modifies them, the modification only occurs in the new scope. To pass by
value, name the variables accordingly and omit from the GOSUB statement; they
will be located and modified in the next scope up.

┌─────────────────────┐
│ Variables and scope │
└─────────────────────┘

LittleScript has both global and local variables, differentiated by the case
of the first character (uppercase = global, lowercase or not a letter = local).
A new local frame is pushed on GOSUB, and deleted on RETURN.

┌──────────────────┐
│ Native functions │
└──────────────────┘

(TODO)


┌───────┐
│ Files │
└───────┘

Many LittleScript systems will not have a traditional file storage, but may
want a way to read and write streams. The OPEN, CLOSE, READ, and WRITE keywords
defer to callbacks provided to LittleScript by the implementing system. The
following syntax is proposed for stream descriptors:

COMn [speed] [parity] [data]
FILE device /path/to/file

┌────────────┐
│ Statements │
└────────────┘

KEYWORD		DOES
ABS(n)
		Return the absolute value of n
ASC(s$[, n])
		Return the byte value of the first or nth character
		of s$
CALL fun(...)
		Call native function `fun` with arguments ...
		See NATIVE FUNCTIONS for the calling convention. `fun`
		must be a literal.
CAT s$ t$
		Concatenate string t$ onto the end of s$.
CHR s$ n
		Append the byte value n to the end of string s$.
CLOSE [#n[,#n]...]
		Close file numbers (or all files, if none specified).
		File (or stream) access is provided by the system
		implementing LittleScript and may not be available.
DATA expr[ expr...]
		Data for READ
DEF FN name[(args...)] expr
		Create a function with the given name.
END
		Stop execution and return to caller
ERASE ident [ident ...]
		Deallocate any space held by ident and remove that identifier.
		ident may point at a list, a string, a function defined by
		DEF FN, or any other variable. Note that because LittleScript
		is not garbage collected unlike BASIC, you must ERASE anything
		you create if you do not want to leak memory (with the
		exception that everything created in a stack frame is erased
		when that stack frame is popped).
FN name[(args...)]
		Execute the function specified by `name`, returning its value.
FOR ident = start TO end [STEP n]
NEXT [ident]
		Iterate ident from start to end by n (default=1).
		When END is encountered, return back if the
		terminating conditions have not been met. If ident
		is not specified for END, walk outward from the inner
		loop, repeating on the first one that has not
		terminated.

		Start and end values are restricted to signed
		16 bit.
FREE ident[ ident ...]
		Deallocate any space held by ident and remove that
		identifier. Note that LittleScript is not garbage
		collected - if you're done with a string, free it.
GOSUB label[(arg[, arg, ...])] [AS ident]
		Push a stack frame and jump to the label. On return,
		if a value was returned, store it in ident.

		If args are specified, they are added to the function's local
		scope.
GOTO label
		Go directly to the label
HEX s$ n
OCT s$ n
DEC s$ n
		Append the hexadecimal, octal, or decimal representation of n
		to s$.
IF cond THEN ...
IF cond GOTO ...
		If cond is true, perform the action.
		cond is an expression, and is true if nonzero.
INPUT #n [AT ?] [UNTIL b] [COUNT n], ident [, ident, ...]
		Read from a stream into variables. Generally only
		string variables are supported, but this is
		implementation-defined. Stop at either the byte
		value b (if UNTIL specified) or the byte count n
		(if COUNT specified), whichever comes first.
		AT is interpreted as for WRITE.
LEN(s$)
		Return the length of the string
LET ident [=] val
		Assign the value of a variable. This is the default
		keyword; statements lacking a keyword are parsed
		as LET.
OPEN s$ AS #n
		Open a file/stream. The "path" is more likely to be
		some form of stream descriptor (see FILES), and may
		be a literal or a string variable.
PACK n[, n...] AS s$ [: fmt]
UNPACK n[, n...] FROM s$ [: fmt]
		Pack or unpack the provided values in binary format
		into/from a string, with specific format optionally
		specified. This can be used to assemble arbitrary
		binary data. See PACK AND UNPACK.
PRINT [TO #n] val [... val...] [,]
		Print values, to a stream or to standard output.
		If the comma is provided, omit newline.
RANDOMIZE [seed]
		Initialize the PRNG. If seed is not specified, it will be
		initialized with the system's preferred random seed source.
		Not all systems guarantee good quality seeds.
READ ident[ ident...]
		Read values from DATA statements into the identifiers
		given. Because LittleScript doesn't always have
		access to the entire program at once like BASIC does,
		it works a little differently. By default, the read
		pointer is uninitialized; on the first READ, the
		interpreter will search *forward* for the first DATA
		statement it finds. Once it *executes* that DATA
		statement, the read pointer is reset back to the
		uninitialized state. Every stack context has its own
		read pointer.
REM
		Comment. Comments may also start with ', and ' may
		appear anywhere in a line.
RESTORE [label]
		Reset the read pointer. If label is specified, set it
		to the next DATA statement after that label.
		Otherwise, set it to the uninitialized state.
RETURN [expr]
		Pop a stack frame. If the previous GOSUB specified a return
		location using AS, return expr into it.
RND
		Return a random number from 0 to 65535.
SWAP m, n
		Swap the contents of two variables or list indices
VAL(s$)
		Returns the numerical value of a string
WHILE cond
WEND
		Loop as long as cond is true (nonzero).
WRITE #n [AT ?] val [val ...]
		Write values to a stream. The interpretation of types
		depends on the stream implementation. If AT is
		specified, this implementation-defined value is also
		passed to the implementation (it is usually an
		address, for interfaces like I2C).

┌──────────┐
│ Keywords │
└──────────┘

Following is the full list of keywords, and the single-byte abbreviations that
may be used in their place.

(TODO: update the abbrev values)

ABBREV	KEYWORD
80	ABS
81	AS
82	ASC
83	AT
84	ATN	(not implemented, reserved)
85	CALL
86	CAT
87	CHR
88	CLOSE
89	COS	(not implemented, reserved)
8A	COUNT
8B	DATA
	DEF
8C	END
	ERASE
8D	ERROR
	FN
8E	FOR
91	GOSUB
92	GOTO
93	IF
94	INPUT
95	LEFT
96	LET
97	LOG	(not implemented, reserved)
98	MID
99	NEXT
9A	ON
9B	OPEN
9C	PACK
9E	PRINT
A0	RANDOMIZE
A1	READ
A2	REM
A3	RESTORE
A4	RETURN
A5	RIGHT
A6	RND
A7	SIN	(not implemented, reserved)
A8	SQR	(not implemented, reserved)
A9	STEP
AA	SWAP
AB	TAN	(not implemented, reserved)
AC	THEN
AD	TO
AE	UNPACK
AF	UNTIL
B0	VAL
B1	WEND
B2	WHILE
B3	WRITE