Stargaze user manual
Table of Contents
(very early draft)
1. Lexical syntax
1.1. Comment
Comment in Stargaze starts with ;
.
1.2. Booleans
Only the strings #t
and #f
are boolean values; they evaluate to true and false respectively.
1.3. Strings
There's only one kind of syntax for string literals: "{string-piece}*"
, where string-piece
is one of:
- Normal characters.
- Escape sequences, which starts with
\
and is of the following format:\r
,\n
,\b
,\t
,\\
,\"
,\v
,\f
, for carriage return, linefeed, backspace, horizontal tab, slash, double quote, vertical tab and form feed respectively.\xHEX
, whereHEX
is the codepoint of character in hexadecimal, e.g.\x61
is the same asa
.
1.4. Characters
Two kinds of syntax for character literals:
#\NAME
whereNAME
is the name for the character.NAME
is often the character itself (e.g.#\a
refers to the charactera
), but exceptions exist; see below.#ch{HEX}
whereHEX
is the codepoint in hexadecimal, e.g.#ch{61}
is the same as#\a
.
As for the first kind, Scheme R4RS only explicitly declares a #\space
and a #\newline
. Stargaze (partly) follows the convention in MIT Scheme, except that #\newline
always corresponds to the ASCII LF regardless of the operating system. For this reason the following "character name"s are also defined in Stargaze:
All of the standard ASCII names for non-printing characters are supported. They can be used by prefixing with
#\
, e.g.#\NUL
:NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US DEL
- A few other names are also defined:
#\esc
, for#\ESC
. MIT Scheme used the string#\altmode
, which we#\backspace
for#\BS
#\linefeed
for#\LF
(also, as stated above,#\newline
)#\tab
for#\HT
#\return
for#\CR
1.5. Vector
Vector literals can be constructed by surrounding values with #{
and }
.
1.6. Quotes
Four kinds of quote exists in Stargaze:
quote
/'
, which turns whatever it prefixed into "the thing itself"; e.g.(if #t 3 4)
evaluates to3
but'(if #t 3 4)
evaluates to(if #t 3 4)
(that is, a list containingif
,#t
,3
and4
.)qquote
/`
, which works like normal quote, but any of the sub-expression within can be "unquoted" and thus get evaluated like normal.unquote
/,
, which evaluates whatever it prefixed within aquasiquote
-ed context. e.g.`(if (leq 3 4) 3 4)
evaluates to(if (leq 3 4) 3 4)
but`(if ,(leq 3 4) 3 4)
evaluates to(if #t 3 4)
.
These quotes are equivalent to their s-expr counterparts at all time, e.g. ''a
would be equivalent to (quote (quote a))
which would evaluates to (quote a)
(which also means that (list? ''a)
would evaluate to #t
.)
2. Built-in primitives
(def NAME BODY)
: Bind the valueBODY
to the nameNAME
in the current environment.(def (FUNCNAME ARGS ...) BODY)
: Short hand for(def FUNCNAME (fn (ARGS ...) BODY))
.
(fn ARGLIST BODY)
: Anonymous function.(if COND THEN_CLAUSE ELSE_CLAUSE)
: Simple branching.(cond (COND1 CLAUSE1) ...)
: Multiway branching.(let ((NAME1 EXP1) ...) BODY ...)
:let
-binding.(letrec ((NAME1 EXP1) ...) BODY ...)
:letrec
-binding.(quote ...)
: Quote its argument as symbolic values(set! NAME VALUE)
: AssignVALUE
to the nameNAME
.(begin EXP1 ...)
: ExecutesEXP1
and the rest in the order they appear in the argument list. Returns the evaluated value of the last argument..(equal EXP1 EXP2)
: Check ifEXP1
andEXP2
has the same value.
2.1. Module-related
(include STR)
: Include a file.(import MOD RENAME?)
:MOD
can be one of two options:- A string, which refers to the module.
(STR PREFIX)
, which prefix all exported name inSTR
withPREFIX
.- e.g. assumes that
mymodule
exportsfunc1
,func2
andfunc3
;(import ("mymodule" xyz))
would importfunc1
asxyzfunc1
,func2
asxyzfunc2
,func3
asxyzfunc3
.
- e.g. assumes that
RENAME
is one of two followings:- Nothing, in which case the form would be
(import MOD)
; this would import all exported name inMOD
. - A list of
(NAME NEWNAME)
, which would only import the exported names described byNAME
, which would bind toNEWNAME
when importing. This renaming takes precedence over prefixes, so(import ("abc" abc) (myfunc1 defmyfunc1))
would importmyfunc1
from module"abc"
intodefmyfunc1
instead ofabcmyfunc1
orabcdefmyfunc1
.
- Nothing, in which case the form would be
- Note that
(import MOD)
means import all names fromMOD
, while(import MOD ())
means import all the exported names in()
, which means none of the exported names.
in the imported module; NEW_NAME
would be the effective name in the importing module for NAME
in the imported module.
(export NAME ...)
: Export
2.2. Miscellaneous
(atom? EXP)
: Check ifEXP
is an atom, i.e. a symbol, an integer, a boolean, a string, a character, or the empty list.(closure? EXP)
: Check ifEXP
is a closure.(primitive? EXP)
: Check ifEXP
is a primitive.(procedure? EXP)
: Check ifEXP
is a closure or primitive.- Note that in Stargaze lingo a "primitive" refers to primitives that can be applied like a function (unlike things like
fn
andquote
, which are called "special forms" and are called "syntax forms" in other LISP-like languages) and a "closure" refers to any anonymous function created during the course of execution.
- Note that in Stargaze lingo a "primitive" refers to primitives that can be applied like a function (unlike things like
2.3. Integer
(int? EXP)
: Check ifEXP
is an integer.(add EXP ...)
: Return the sum of the arguments.(sub EXP1 ...)
: ReturnEXP1 - EXP2 - ...
.(mul EXP ...)
: Return the product of the arguments.(div EXP1 ...)
: ReturnEXP1 div EXP2 div ...
(mod EXP1 EXP2)
: ReturnEXP1 % EXP2
.(leq EXP1 EXP2)
,(lt EXP1 EXP2)
,(geq EXP1 EXP2)
,(gt EXP1 EXP2)
: Check ifEXP1
is smaller or equal than, strictly smaller than, greater or equal than, and strictly greater thanEXP2
respectively.(intstr EXP)
: convertEXP
into a string.
2.4. Floating-point
(float? EXP)
: Check ifEXP
is an floating point number.(addf EXP ...)
: Return the sum of the arguments.(subf EXP1 ...)
: ReturnEXP1 - EXP2 - ...
.(mulf EXP ...)
: Return the product of the arguments.(divf EXP1 ...)
: ReturnEXP1 div EXP2 div ...
(float INT)
: ConvertINT
to a floating point number.- Returns the argument itself if it is already a floating-point number.
(floor FLOAT)
,(ceil FLOAT)
,(round FLOAT)
,(trunc FLOAT)
:floor
returns the first integer that's smaller than or equal to its arugment.ceil
returns the first integer that's bigger than or equal to its argument.round
rounds its argument to the nearest integer.trunc
directly removes the decimal part.- Returns the argument itself if it is already an integer.
(leqf EXP1 EXP2)
,(ltf EXP1 EXP2)
,(geqf EXP1 EXP2)
,(gtf EXP1 EXP2)
: Check ifEXP1
is smaller or equal than, strictly smaller than, greater or equal than, and strictly greater thanEXP2
respectively.(floatstr EXP)
: convertEXP
into a string.(eqnum EXP1 EXP2)
: Return ifEXP1
andEXP2
has the same numerical value.
2.5. Pair
(cons EXP1 EXP2)
: Return the pair ofEXP1
andEXP2
(car EXP1)
: Return the first component of the pairEXP1
.(cdr EXP1)
: Return the second component of the pairEXP2
.(set-car! EXP NEWCAR)
: Set the first component ofEXP
toNEWCAR
.(set-cdr! EXP NEWCDR)
: Set the second component ofEXP
toNEWCAR
.(w/car EXP NEWCAR)
: Equivalent to(cons NEWCAR (cdr EXP))
.(w/cdr EXP NEWCDR)
: Equivalent to(cons (car EXP) NEWCDR)
.
2.6. Character
(chr INT)
: ConvertINT
into the corresponding character.(ord CHAR)
: ConvertCHAR
into the corresponding integer.(char? EXP)
: Check ifEXP
is a character.
2.7. String
(strref STR I)
: Retrieve theI
-th (starting from 0) character of the stringSTR
.(substr STR START END?)
: Retrieve the substring of the stringSTR
starting from indexSTART
to indexEND
.END
is optional; if it's not provided, this function takes the substring starting from indexSTART
to the end of the string.(strsym STR)
: Convert a string to a symbol.(str? EXP)
: Check ifEXP
is a string.
2.8. Boolean
(and EXP1 ...)
: Returns#f
if one of theEXP
evalueates to#f
, or else returns the last argument.(or EXP1 ...)
: Returns the first value that is not#f
(if any); or else, returns#f
.(not EXP)
: IfEXP
evaluates to#f
then return#t
; returns#f
otherwise.
2.9. Symbol
(symstr SYM)
: Convert a symbol to a string.(sym? EXP)
: Check ifEXP
is a symbol.
2.10. List
(list EXP1 ...)
: Combine its arguments into a list.(nil? EXP)
: Check ifEXP
is an empty list.
2.11. Vector
(vec? EXP)
: Check ifEXP
is a vector.(vector EXP1 ...)
: Combine its arguments into a vector.(listvec LIST)
: ConvertLIST
into a vector.(veclist VEC)
: ConverVEC
into a list(vecref VEC INT)
: Return theINT
-th element fromVEC
.(mkvec INT)
: Create a vector of sizeINT
.(vecset! VEC INT VALUE)
: Set theINT
-th element ofVEC
to valueVALUE
.(vec++ EXP1 ...)
:(veclen VECTOR)
:
2.12. File input/output
stdin
,stdout
,stderr
: Standard input, standard output and standard error.(eof? EXP)
: Check ifEXP
is the EOF object.(openinput STR)
: Open fileSTR
as input.(openoutput STR MODE?)
: Open fileSTR
as output.- When
MODE
is not used (i.e. the form is(openoutput STR)
), the file is opened for writing. - When
MODE
is"a"
, the file is opened for appending.
- When
(close FILE)
: Close a file.(readch FILE)
: Read a character from an input file.(writech FILE)
: Write a character to an output file.
2.13. Iteration
Iteration is important (at least for now) since we don't have tail-call optimization.
(while COND BODY)
: Repeatedly executeBODY
untilCOND
evaluates to false. Returns nil.
2.14. Structure operations
(defstruct NAME DESCRIPTOR...)
: Define a "struct".(defstruct NAME FIELD1 ...)
: Define a simple record type.(defstruct NAME (TAGSYMBOL1 FIELD11 ...) ...)
: Define a struct that has multiple branch, similar to the algebraic datatypes / disjoint union in languages like Haskell and Standard ML.
When executed, a few new functions would be defined in the call site's environment:
(NAME? EXP)
: Check ofEXP
evaluates to a value that is a struct of typeNAME
.(NAME EXP1 ...)
: Constructor for the struct typeNAME
. Defined only whenNAME
is defined as a simple record type.(NAME/FIELD1 EXP)
, …: Field getters for the struct typeNAME
. Defined only whenNAME
is defined as a simple record type.(TAGSYMBOL1 EXP1 ...)
, …: Constructors for the struct typeNAME
. Defined only whenNAME
is defined as a disjoint union.(NAME/TAGSYMBOL1/FIELD1 EXP)
, …: Field getters for the struct typeNAME
. Defined only whenNAME
is defined as a disjoint union.
For example, this:
(defstruct MyStruct fieldName1 fieldName2)
would have these functions defined:
(MyStruct? EXP) (MyStruct EXP1 EXP2) (MyStruct/fieldName1 EXP) (MyStruct/fieldName2 EXP)
and it would be like this in Haskell:
-- the name "MyStructConstructor1" itself is not important here because -- for Stargaze when the definition has only 1 branch this part is considered -- to be not exist. data MyStruct = MyStructConstructor1 { fieldName1 :: Field1Type, fieldName2 :: Field2Type }
or this in Nim:
type MyStruct = ref object fieldName1: Field1Type fieldName2: Field2Type
while this:
(defstruct Term (TInt iVal) (TVar vName) (TApp appHead appArgs) (TLambda lArgs lBody))
would have the following functions defined:
(Term? EXP) (TInt EXP) (TVar EXP) (TApp EXP EXP) (TLambda EXP EXP) (Term/TInt/iVal EXP) (Term/TVar/vName EXP) (Term/TApp/appHead EXP) (Term/TApp/appArgs EXP) (Term/TLambda/lArgs EXP) (Term/TLambda/lBody EXP)
and it would be like this in Haskell:
data Term = TInt Int | TVar String | TApp Term [Term] | TLambda [String] Term
and this in Nim:
type TermType* = enum TInt TVar TApp TLambea Term* = ref object case tType*: TermType of TInt: iVal*: int of TVar: vName*: string of TApp: appHead*: Term appArgs*: seq[Term] of TLambda: lArgs*: seq[string] lBody*: Term
Note that these two following examples are different: one is a simple record type, one is a disjoint union that has only 1 branch:
;; create a struct called "MyStruct" with 2 fields ;; this is similar to this one in Haskell: ;; type MyStruct = (Field1Type, Field2Type) ;; myStruct_field1 :: MyStruct -> Field1Type ;; myStruct_field2 :: MyStruct -> Field2Type ;; myStruct_field1 (r, _) = r ;; myStruct_field2 (_, r) = r (defstruct MyStruct field1 field2) ;; create a struct called "MyStruct" with 2 fields ;; this is like the following in Haskell: ;; data MyStruct = MSConstr1 { field1 :: Field1Type, field2 :: Field2Type } (defstruct MyStruct (MSConstr1 field1 field2)) ;; create a struct called "MyStruct" with 1 fields since the first symbol ;; is considered to be a tag. This is like the following in Haskell: ;; data MyStruct = Field1 Field2Type (defstruct MyStruct (Field1 Field2)
(struct? EXP)
: Check ifEXP
evaluates to a struct value.(struct-major-label EXP)
: IfEXP
evaluates to a struct, return its "major label", which would be theNAME
part of the struct.(struct-secondary-label EXP)
: IfEXP
evaluates to a struct, return its "secondary label", which woould be theTAG-SYMBOL1
part of the struct.
For example, when we have the following definition:
(defstruct mystruct1 field1 field2) (defstruct mystruct2 (MSConstr1 field1 field2)) (defstruct mystruct3 (Field1 Field2))
The following expressions all evaluates to a struct value:
(mystruct1 3 4) (mystruct2 'MSConstr1 3 4) (mystruct3 'Field1 4) (mystruct3 (strsym "Field1") 4) (mystruct3 (strsym (str++ "Fie" "ld" "1")) 4)
while these raises error:
(mystruct1 3) ;; missing one field (mystruct1 4 5 6) ;; one field too many (mystruct2 'MSConstr2 3 4) ;; tag not allowed; must be MSConstr1, but MSConstr2 found. (mystruct3 3 4) ;; tag not allowed; must be 'Field1, but 3 found.
The "major label" of the examples above would be:
(struct-major-label (mystruct1 3 4)) ;; 'mystruct1 (struct-major-label (mystruct2 'MSConstr1 3 4)) ;; 'mystruct2 (struct-major-label (mystruct3 'Field1 4)) ;; 'mystruct3 (struct-major-label (mystruct3 (strsym "Field1") 4)) ;; 'mystruct3 (struct-major-label (mystruct3 (strsym (str++ "Fie" "ld" "1")) 4)) ;; 'mystruct3
and the "secondary label" of the examples above would be:
(struct-secondary-label (mystruct1 3 4)) ;; #f (not applicable for simple struct) (struct-major-label (mystruct2 'MSConstr1 3 4)) ;; 'MSConstr1 (struct-major-label (mystruct3 'Field1 4)) ;; 'Field1 (struct-major-label (mystruct3 (strsym "Field1") 4)) ;; 'Field1 (struct-major-label (mystruct3 (strsym (str++ "Fie" "ld" "1")) 4)) ;; 'Field1
(struct-arity EXP)
: IfEXP
evaluates to a struct, return its arity (i.e. the number of fields, major label & secondary label not included); or else, return #f.
Tags are not counted. Using the example above, the following to expressions both evaluate to the number 2:
(struct-arity (mystruct1 3 4)) ;; 2 (struct-arity (mystruct2 'MSConstr1 3 4)) ;; 2 ;; not 3, because 'MSConstr1 is not counted
(structvec EXP)
: ConvertEXP
into a vector, which would be its field list (in order) with its major label & secondary label at the front. For example:
(structvec (mystruct1 3 4)) ;; #{'mystruct1 #f 3 4} (structvec (mystruct2 'MSConstr1 3 4)) ;; #{'mystruct2 'MSConstr1 3 4} (structvec (mystruct3 'Field1 4)) ;; #{'mystruct3 'Field1 4} (structvec (mystruct3 (strsym "Field1") 4)) ;; #{'mystruct3 'Field1 4} (structvec (mystruct3 (strsym (str++ "Fie" "ld" "1")) 4)) ;; #{'mystruct3 'Field1 4}
(vecstruct EXP)
: ConvertEXP
(which should be a vector) into a struct. The vector should be in the form of#{majorLabel secondaryLabel field ...}
. Basicallystructvec
's inverse action; see examples above.
3. Extended primitives
A few functions that should be able to be defined using the language itself is defined as primitives for performance, even if there aren't much performance to begin with…
3.1. List libraries
(length LIST)
: Return the length ofLIST
.(append EXP ...)
/(list++ EXP ...)
: CombinesEXP
and the rest into one list.(reverse LIST)
: Return a reversed version ofLIST
.(map F LIST1 ...)
: Returns a new list whose members are the results of applyingF
onLIST1
and the rest, e.g.(map add (list 3 4) (list 5 6) (list 7 8))
is equivalent to(list (add 3 5 7) (add 4 6 8))
.(filter F LIST)
: Returns a new list consisting of all the members ofLIST
that satisfiesF
.F
should be a function that takes 1 argument and returns a boolean.(member EXP LIST)
: Check ifEXP
is in the listLIST
. If it is, returns the part ofLIST
starting fromEXP
; if not, return the false value.(assoc EXP LIST)
: Check ifEXP
is in the assoc listLIST
. An assoc list in LISP-like languages is a kind of list that consists of key-value pairs. If there is a key-value pair that usesEXP
as the key, this primitive will return that key-value pair; or else, it will return the false value.
3.2. Bitwise operations
(bit~ INT)
: Bitwise not.(bit& INT ...)
: Bitwise and.(bit^ INT ...)
: Bitwise xor.(bit| INT ...)
: Bitwise or.(bit<< INT INT)
: Left shift.(bit>> INT INT)
: Right shift (left-side padded with 0)
3.3. String operations
(mkstr N CHAR)
: Makes a string of lengthN
consisting of only characterCHAR
.(strlen STR)
: Returns the length of the stringSTR
.(strlist STR)
: Convert a string to a list of characters.(strvec STR)
: Convert a string to a vector of characters.(str++ STR1 ...)
: ConcatenatesSTR1
and the rest into one single string.