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, whereHEXis the codepoint of character in hexadecimal, e.g.\x61is the same asa.
1.4. Characters
Two kinds of syntax for character literals:
#\NAMEwhereNAMEis the name for the character.NAMEis often the character itself (e.g.#\arefers to the charactera), but exceptions exist; see below.#ch{HEX}whereHEXis 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#\backspacefor#\BS#\linefeedfor#\LF(also, as stated above,#\newline)#\tabfor#\HT#\returnfor#\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 to3but'(if #t 3 4)evaluates to(if #t 3 4)(that is, a list containingif,#t,3and4.)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 valueBODYto the nameNAMEin 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): AssignVALUEto the nameNAME.(begin EXP1 ...): ExecutesEXP1and the rest in the order they appear in the argument list. Returns the evaluated value of the last argument..(equal EXP1 EXP2): Check ifEXP1andEXP2has the same value.
2.1. Module-related
(include STR): Include a file.(import MOD RENAME?):MODcan be one of two options:- A string, which refers to the module.
(STR PREFIX), which prefix all exported name inSTRwithPREFIX.- e.g. assumes that
mymoduleexportsfunc1,func2andfunc3;(import ("mymodule" xyz))would importfunc1asxyzfunc1,func2asxyzfunc2,func3asxyzfunc3.
- e.g. assumes that
RENAMEis 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 toNEWNAMEwhen importing. This renaming takes precedence over prefixes, so(import ("abc" abc) (myfunc1 defmyfunc1))would importmyfunc1from module"abc"intodefmyfunc1instead ofabcmyfunc1orabcdefmyfunc1.
- 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 ifEXPis an atom, i.e. a symbol, an integer, a boolean, a string, a character, or the empty list.(closure? EXP): Check ifEXPis a closure.(primitive? EXP): Check ifEXPis a primitive.(procedure? EXP): Check ifEXPis a closure or primitive.- Note that in Stargaze lingo a "primitive" refers to primitives that can be applied like a function (unlike things like
fnandquote, 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 ifEXPis 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 ifEXP1is smaller or equal than, strictly smaller than, greater or equal than, and strictly greater thanEXP2respectively.(intstr EXP): convertEXPinto a string.
2.4. Floating-point
(float? EXP): Check ifEXPis 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): ConvertINTto a floating point number.- Returns the argument itself if it is already a floating-point number.
(floor FLOAT),(ceil FLOAT),(round FLOAT),(trunc FLOAT):floorreturns the first integer that's smaller than or equal to its arugment.ceilreturns the first integer that's bigger than or equal to its argument.roundrounds its argument to the nearest integer.truncdirectly 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 ifEXP1is smaller or equal than, strictly smaller than, greater or equal than, and strictly greater thanEXP2respectively.(floatstr EXP): convertEXPinto a string.(eqnum EXP1 EXP2): Return ifEXP1andEXP2has the same numerical value.
2.5. Pair
(cons EXP1 EXP2): Return the pair ofEXP1andEXP2(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 ofEXPtoNEWCAR.(set-cdr! EXP NEWCDR): Set the second component ofEXPtoNEWCAR.(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): ConvertINTinto the corresponding character.(ord CHAR): ConvertCHARinto the corresponding integer.(char? EXP): Check ifEXPis 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 stringSTRstarting from indexSTARTto indexEND.ENDis optional; if it's not provided, this function takes the substring starting from indexSTARTto the end of the string.(strsym STR): Convert a string to a symbol.(str? EXP): Check ifEXPis a string.
2.8. Boolean
(and EXP1 ...): Returns#fif one of theEXPevalueates 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): IfEXPevaluates to#fthen return#t; returns#fotherwise.
2.9. Symbol
(symstr SYM): Convert a symbol to a string.(sym? EXP): Check ifEXPis a symbol.
2.10. List
(list EXP1 ...): Combine its arguments into a list.(nil? EXP): Check ifEXPis an empty list.
2.11. Vector
(vec? EXP): Check ifEXPis a vector.(vector EXP1 ...): Combine its arguments into a vector.(listvec LIST): ConvertLISTinto a vector.(veclist VEC): ConverVECinto 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 ofVECto valueVALUE.(vec++ EXP1 ...):(veclen VECTOR):
2.12. File input/output
stdin,stdout,stderr: Standard input, standard output and standard error.(eof? EXP): Check ifEXPis the EOF object.(openinput STR): Open fileSTRas input.(openoutput STR MODE?): Open fileSTRas output.- When
MODEis not used (i.e. the form is(openoutput STR)), the file is opened for writing. - When
MODEis"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 executeBODYuntilCONDevaluates 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 ofEXPevaluates to a value that is a struct of typeNAME.(NAME EXP1 ...): Constructor for the struct typeNAME. Defined only whenNAMEis defined as a simple record type.(NAME/FIELD1 EXP), …: Field getters for the struct typeNAME. Defined only whenNAMEis defined as a simple record type.(TAGSYMBOL1 EXP1 ...), …: Constructors for the struct typeNAME. Defined only whenNAMEis defined as a disjoint union.(NAME/TAGSYMBOL1/FIELD1 EXP), …: Field getters for the struct typeNAME. Defined only whenNAMEis 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 ifEXPevaluates to a struct value.(struct-major-label EXP): IfEXPevaluates to a struct, return its "major label", which would be theNAMEpart of the struct.(struct-secondary-label EXP): IfEXPevaluates to a struct, return its "secondary label", which woould be theTAG-SYMBOL1part 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): IfEXPevaluates 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): ConvertEXPinto 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 ...): CombinesEXPand 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 applyingFonLIST1and 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 ofLISTthat satisfiesF.Fshould be a function that takes 1 argument and returns a boolean.(member EXP LIST): Check ifEXPis in the listLIST. If it is, returns the part ofLISTstarting fromEXP; if not, return the false value.(assoc EXP LIST): Check ifEXPis 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 usesEXPas 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 lengthNconsisting 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 ...): ConcatenatesSTR1and the rest into one single string.