This article is openly licensed via CC BY-NC-ND 4.0.
Project Address: https://github.com/shajunxing/banana-script
Introduction
My goal is to remove and modify useless and ambiguous parts of JavaScript language that I've summarized in practice, and to create a minimal syntax interpreter by keeping only what I like and need. Only JSON-compatible data types and function are supported, function is first-class value, and function supports closure. I don't like object-oriented programming, so everything class related are not supported. There are no built-in immunable global variables, global functions, or object members, even contents added during interpreter initialization can be easily deleted at any time and reverted to clean empty state.
Two-Minute Brief Syntax Guide for Proficient JavaScript Users
Data types are null
boolean
number
string
array
object
function
, results of typeof
correspond strictly to these names. No undefined
because null
is enough. Array and object are clean, no predefined members such as __proto__
.
Variable declaraction use let
, all variables are local, const
is not supported because all must be deletable. Access undeclared variables will cause error, access array/object's unexisting members will get null
, and put null
will delete corresponding member.
Function definition supports default parameter param = value
and rest parameter ...params
. Array literal and function call support spread syntax ...
, which will not skip null
members. No predefined members such as this
arguments
in function. If return
is outside function, means exit vm.
Operators follow strict rule, no implicit conversion. Only boolean can do logical operations. == !=
are strict meaning, and can be done by all types. Strings can do all relational operations and +
. Numbers can do all relational and numerical operations. Operator precedence from low to high is:
- Ternary operator
?
:
- Logical or operator
||
- Logical and operator
&&
- Relational operator
==
!=
<
<=
>
>=
- Additive operator
+
-
- Multiplicative operator
*
/
%
- Exponential operator
**
- Prefix operator
+
-
!
typeof
- Array/object member access and function call operator
[]
.
?.
()
Assignment expression =
+=
-=
*=
/=
%=
++
--
does not return value, Comma expression ,
is not supported.
Conditional statement is if
, loops are while
do while
for
, conditions must be boolean. for
loop only support following syntax, []
means optional. for in
and for of
only handle non-null members:
for ([[let] variable = expression ] ; [condition] ; [assignment expression])
for ([let] variable in array/object)
for ([let] variable of array/object)
No modules. In inperpreter's view, source code is only one large flat text.
Garbage collection is manual, you can do it at any time you need.
delete
means delete local variable within current scope (object members can be deleted by setting null
). For example, variables added to the function closure are all local variables before return, so unused variables can be delete
d before return to reduce closure size, run following two statements in REPL environment to see differences.
let f = function(a, b){let c = a + b; return function(d){return c + d;};}(1, 2); dump(); print(f(3)); delete f;
let f = function(a, b){let c = a + b; delete a; delete b; return function(d){return c + d;};}(1, 2); dump(); print(f(3)); delete f;
throw
can throw any value, which are received by catch
. finally
is not supported, because I think it's totally unecessary, and will make code execution order weird.
Technical internals
This project is C99 compatable, no other dependences, even make systems are not necessary, only need C compiler, compilation environment is msgc/gcc/mingw. First, from https://github.com/shajunxing/banana-nomake download single file make.h
, then open make.c
, modify #include
to correct path, then with msvc type cl make.c && make.exe release
, or with mingw type gcc -o make.exe make.c && ./make.exe release
. Executables are in bin
folder.
Project follows "minimal dependency" rule, only including necessary headers. Also, there's only one-way referencing between modules, with no circular referencing. Here’s how modules work and their dependencies:
js-common js-data js-vm js-syntax
<-----------
<-----------
<-----------
<-----------------------
<-----------------------------------
-
js-common
: Constants, macro definitions, and functions common to project, such as log, memory io -
js-data
: Data types and garbage collection, you can even use this module separately in C projects to manipulate high-level data structures with GC functionality, see https://github.com/shajunxing/banana-cvar -
js-vm
: Bytecode Virtual Machine, compiled separately to get an interpreter with minimal footprint without source code parsing -
js-syntax
: Lexical parsing and syntax parsing, which converts source code into bytecode
All values are struct js_value
type, you can create by js_xxx()
functions, xxx
is value type, and you can read c values direct from this struct, see definition in js_data.h
. Created values follow garbage collecting rules. DON'T directly modify their content, if you want to get different values, create new one. Compound types array
object
can be operated by js_array_xxx()
js_object_xxx()
functions.
C functions must be struct js_result (*)(struct js_vm *)
format, use js_c_function()
to create c function value, yes of course they are all values and can be put anywhere, for example, if put on stack root using js_variable_declare()
, they will be global. struct js_result
has two members, if .success
is true, .value
is return value, if false, .value
is received by catch
if there are try catch
. c function can also call script function using js_call()
. Inside C function, use js_parameter_base()
js_parameter_length()
js_parameter_get()
to get passed in parameters.
Top comments (0)