mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 17:50:22 +00:00
Add wisp language support.
This commit is contained in:
367
samples/wisp/intro.wisp
Normal file
367
samples/wisp/intro.wisp
Normal file
@@ -0,0 +1,367 @@
|
||||
;; # wisp
|
||||
|
||||
; Wisp is homoiconic JS dialect with a clojure syntax, s-expressions and
|
||||
; macros. Wisp code compiles to a human readable javascript, which is one
|
||||
; of they key differences from clojurescript.
|
||||
|
||||
;; ## wisp data structures
|
||||
|
||||
;; 1. nil - is just like js undefined with a differenc that it's
|
||||
;; not something can be defined. In fact it's just a shortcut for
|
||||
;; void(0) in JS.
|
||||
nil ;; => void(0)
|
||||
|
||||
;; 2. Booleans - Wisp booleans true / false are JS booleans
|
||||
|
||||
true ;; => true
|
||||
|
||||
;; 3. Numbers - Wisp numbers are JS numbers
|
||||
1 ;; => 1
|
||||
|
||||
;; 4. Strings - Wisp strings are JS Strings
|
||||
"Hello world"
|
||||
;; Wisp strings can be multiline
|
||||
"Hello,
|
||||
My name is wisp!"
|
||||
|
||||
;; 5. Characters - Characters are sugar for JS single char strings
|
||||
\a ;; => "a"
|
||||
|
||||
;; 6. Keywords - Keywords are symbolic identifiers that evaluate to
|
||||
;; themselves.
|
||||
:keyword ;; => "keyword"
|
||||
;; Since in JS string constats fulfill this purpose of symbolic
|
||||
;; identifiers, keywords compile to equivalent JS strings.
|
||||
(window.addEventListener :load handler false)
|
||||
;; Keywords can be invoked as functions, that desugars to plain
|
||||
;; associated value access in JS
|
||||
(:bar foo) ;; => foo["bar"]
|
||||
|
||||
|
||||
;; 7. Vectors - Wisp vectors are JS arrays.
|
||||
[ 1 2 3 4 ]
|
||||
;; Note: Commas are white space & can be used if desired
|
||||
[ 1, 2, 3, 4]
|
||||
|
||||
|
||||
;; 8. Maps - Maps are hash maps, plain JS objects. Note that unlike
|
||||
;; in clojure keys can not be of arbitary types.
|
||||
{ "foo" bar :beep-bop "bop" 1 2 }
|
||||
;; Commas are optional but can come handy for separating key value
|
||||
;; pairs.
|
||||
{ a 1, b 2 }
|
||||
;; In a future JSONs syntax may be made compatible with map syntax.
|
||||
|
||||
|
||||
;; 9. Lists - You can't have a lisp without lists! Wisp has lists too.
|
||||
;; Wisp is homoiconic and it's code is made up of lists representing
|
||||
;; expressions. The first item in the expression is a function, being
|
||||
;; invoked with rest items as arguments.
|
||||
(foo bar baz) ; => foo(bar, baz);
|
||||
|
||||
;; # Conventions
|
||||
;; Wisp puts a lot of effort in making naming conventions transparent,
|
||||
;; by encouraning lisp conventions and then translating them to equivalent
|
||||
;; JS conventions:
|
||||
|
||||
(dash-delimited) ;; => dashDelimited
|
||||
(predicate?) ;; => isPredicate
|
||||
(**privates**) ;; => __privates__
|
||||
(list->vector) ;; => listToVector
|
||||
|
||||
;; As a side effect some names can be expressed in a few ways, although
|
||||
;; it's considered to be an advantage.
|
||||
|
||||
(parse-int x)
|
||||
(parseInt x)
|
||||
|
||||
(array? x)
|
||||
(isArray x)
|
||||
|
||||
|
||||
;; # Special forms
|
||||
|
||||
;; There are some functions in wisp that are special, in a sence that
|
||||
;; they compile to JS expressions & can not be passed around as regular
|
||||
;; functions. JS operators are represteted in wisp as special forms
|
||||
|
||||
;; Arithmetic forms - Wisp comes with special form for arithmetic
|
||||
;; operations.
|
||||
|
||||
(+ a b) ; => a + b
|
||||
(+ a b c) ; => a + b + c
|
||||
(- a b) ; => a - b
|
||||
(* a b c) ; => a * b * c
|
||||
(/ a b) ; => a / b
|
||||
(mod a b) ; => a % 2
|
||||
|
||||
;; Comparison forms - Wisp comes with special forms for comparisons
|
||||
|
||||
(identical? a b) ;; => a === b
|
||||
(identical? a b c) ;; => a === b && b === c
|
||||
(= a b) ;; => a == b
|
||||
(= a b c) ;; => a == b && b == c
|
||||
(> a b) ;; => a > b
|
||||
(>= a b) ;; => a >= b
|
||||
(< a b c) ;; => a < b && b < c
|
||||
(<= a b c) ;; => a <= b && b <= c
|
||||
|
||||
;; Logical forms - Wisp comes with special forms for logical operations
|
||||
|
||||
(and a b) ;; => a && b
|
||||
(and a b c) ;; => a && b && c
|
||||
(or a b) ;; => a || b
|
||||
(and (or a b)
|
||||
(and c d)) ;; (a || b) && (c && d)
|
||||
|
||||
|
||||
;; Definitions - Variable definitions also happen through special forms.
|
||||
(def a) ; => var a = void(0);
|
||||
(def b 2) ; => var b = 2;
|
||||
|
||||
;; Assignments - In wisp new values can be set to a variables via `set!`
|
||||
;; special form. Note that in functional programing binding changes are
|
||||
;; a bad practice, avoiding those would make your programs only better!
|
||||
;; Stil if you need it you have it.
|
||||
(set! a 1)
|
||||
|
||||
;; Conditionals - Conditional code branching in wisp is expressed via
|
||||
;; if special form. First expression following `if` is a condition,
|
||||
;; if it evaluates to `true` result of the `if` expression is second
|
||||
;; expression otherwise it's third expression.
|
||||
(if (< number 10)
|
||||
"Digit"
|
||||
"Number")
|
||||
;; Else expression is optional, if missing and conditional evaluates to
|
||||
;; `true` result will be `nil`.
|
||||
(if (monday? today) "How was your weekend")
|
||||
|
||||
|
||||
|
||||
;; Compbining expressions - In wisp is everything is an expression, but
|
||||
;; sometimes one might want to compbine multiple expressions into one,
|
||||
;; usually for the purpose of evaluating expressions that have
|
||||
;; side-effects
|
||||
(do
|
||||
(console.log "Computing sum of a & b")
|
||||
(+ a b))
|
||||
|
||||
;; Also number of expressions is `do` special form 0 to many. If `0`
|
||||
;; result of evaluation will be nil.
|
||||
(do)
|
||||
|
||||
|
||||
;; Bindings - Let special form evaluates containing expressions in a
|
||||
;; lexical context of in which simbols in the bindings-forms (first item)
|
||||
;; are bound to their respective expression results.
|
||||
|
||||
(let [a 1
|
||||
b (+ a c)]
|
||||
(+ a b))
|
||||
|
||||
|
||||
;; Functions - Wisp functions are JS functions
|
||||
(fn [x] (+ x 1))
|
||||
|
||||
;; Wisp functions can be named similar to JS
|
||||
(fn increment [x] (+ x 1))
|
||||
|
||||
;; Wisp functions can also contain documentation and some metadata.
|
||||
;; Note: Docstring and metadata is not presented in compiled JS yet,
|
||||
;; but in a future it will compile to comments associated with function.
|
||||
(fn incerement
|
||||
"Returns a number one greater than given."
|
||||
{:added "1.0"}
|
||||
[x] (+ x 1))
|
||||
|
||||
;; Wisp makes capturing of rest arguments a lot easier than JS. argument
|
||||
;; that follows special `&` simbol will capture all the rest args in array.
|
||||
|
||||
(fn [x & rest]
|
||||
(rest.reduce (fn [sum x] (+ sum x)) x))
|
||||
|
||||
;; Overloads - In wisp functions can be overloaded depending on number
|
||||
;; of arguments they take, without introspection of rest arguments.
|
||||
(fn sum
|
||||
"Return the sum of all arguments"
|
||||
{:version "1.0"}
|
||||
([] 0)
|
||||
([x] x)
|
||||
([x y] (+ x y))
|
||||
([x & more] (more.reduce (fn [x y] (+ x y)) x)))
|
||||
|
||||
;; If function does not has variadic overload and more arguments is
|
||||
;; passed to it, it throws exception.
|
||||
(fn
|
||||
([x] x)
|
||||
([x y] (- x y)))
|
||||
|
||||
|
||||
|
||||
;; ## Other Special Forms
|
||||
|
||||
;; Instantiation - In wisp type instantiation has a consice form, type
|
||||
;; function just needs to be suffixed with `.` character
|
||||
(Type. options)
|
||||
|
||||
;; More verbose but JS like form is also there
|
||||
(new Class options)
|
||||
|
||||
|
||||
;; Method calls - In wisp method calls are no different from function
|
||||
;; calls, it's just method functions are perfixed with `.` character
|
||||
(.log console "hello wisp")
|
||||
|
||||
;; Also more JS like forms are supported too!
|
||||
(window.addEventListener "load" handler false)
|
||||
|
||||
|
||||
;; Attribute access - In wisp attribute access is also just like function
|
||||
;; call. Attribute name just needs to be prefixed with `.-`
|
||||
(.-location window)
|
||||
|
||||
;; Compound properties can be access via `get` special form
|
||||
(get templates (.-id element))
|
||||
|
||||
;; Catching exceptions - In wisp exceptions can be handled via `try`
|
||||
;; special form. As everything else try form is also expression. It
|
||||
;; results to nil if no handling takes place.
|
||||
(try (raise exception))
|
||||
|
||||
;; Although catch form can be used to handle exceptions
|
||||
(try
|
||||
(raise exception)
|
||||
(catch error (.log console error)))
|
||||
|
||||
;; Also finally clase can be used when necessary
|
||||
(try
|
||||
(raise exception)
|
||||
(catch error (recover error))
|
||||
(finally (.log console "That was a close one!")))
|
||||
|
||||
|
||||
;; Throwing exceptions - Throw special form allows throwing exceptions,
|
||||
;; although doing that is not idiomatic.
|
||||
(fn raise [message] (throw (Error. message)))
|
||||
|
||||
|
||||
;; ## Macros
|
||||
;; Wisp has a programmatic macro system which allows the compiler to
|
||||
;; be extended by user code. Many core constructs of Wisp are in fact
|
||||
;; normal macros.
|
||||
|
||||
;; quote
|
||||
|
||||
;; Before diving into macros too much, we need to learn about few more
|
||||
;; things. In lisp any expression can be marked to prevent it from being
|
||||
;; evaluated. For instance, if you enter the symbol `foo` you will be
|
||||
;; evaluating the reference to the value of the corresponding variable.
|
||||
foo
|
||||
|
||||
;; If you wish to refer to the literal symbol, rather then reference you
|
||||
;; could use
|
||||
(quote foo)
|
||||
;; or more usually
|
||||
'foo
|
||||
|
||||
;; Any expression can be quoted, to prevent it's evaluation. Although your
|
||||
;; resulting programs should not have these forms compiled to JS.
|
||||
'foo
|
||||
':bar
|
||||
'(a b)
|
||||
|
||||
;; Wisp doesn’t have `unless` special form or a macro, but it's trivial
|
||||
;; to implement it via macro. Although let's try implemting it as a
|
||||
;; function to understand a use case for macro!
|
||||
|
||||
;; We want to execute body unless condition is `true`.
|
||||
(defn unless-fn [condition body]
|
||||
(if condition nil body))
|
||||
|
||||
;; Although following code will log "should not print" anyway, since
|
||||
;; function arguments are exectued before function is called.
|
||||
(unless-fn true (console.log "should not print"))
|
||||
|
||||
;; Macros solve this problem, because they do not evaluate their arguments
|
||||
;; immediately. Instead, you get to choose when (and if!) the arguments
|
||||
;; to a macro are evaluated. Macros take items of the expression as
|
||||
;; arguments and return new form that is compiled instead.
|
||||
(defmacro unless
|
||||
[condition form]
|
||||
(list 'if condition nil form))
|
||||
|
||||
;; The body of unless macro executes at macro expansion time, producing an
|
||||
;; if form for compilation. Which later is compiled as usual. This way
|
||||
;; compiled JS is a conditional instead of function call.
|
||||
(unless true (console.log "should not print"))
|
||||
|
||||
;; Simple macros like above could be written via templating, expressed
|
||||
;; as syntax-quoted forms.
|
||||
|
||||
;; `syntax-quote` is almost the same as the plain `quote`, but it allows
|
||||
;; sub expressions to be unquoted so that form acts a template. Symbols
|
||||
;; inside form are resolved to help prevent inadvertent symbol capture.
|
||||
;; Which can be done via `unquote` and `unquote-splicing` forms.
|
||||
|
||||
(syntax-quote (foo (unquote bar)))
|
||||
(syntax-quote (foo (unquote bar) (unquote-splicing bazs)))
|
||||
|
||||
;; Also there is a special syntax sugar for both unquoting operators:
|
||||
|
||||
;; Syntax quote: Quote form, but allow internal unquoting so that form
|
||||
;; acts as template. Symbols inside form are resolved to help prevent
|
||||
;; inadvertent symbol capture.
|
||||
`(foo bar)
|
||||
|
||||
;; Unquote: Use inside a syntax-quote to substitute an unquoted value.
|
||||
`(foo ~bar)
|
||||
|
||||
;; Splicing unquote: Use inside a syntax-quote to splice an unquoted
|
||||
; list into a template.
|
||||
`(foo ~bar ~@bazs)
|
||||
|
||||
|
||||
;; For expmale build-in `defn` macro can be defined expressed with
|
||||
;; simple template macro. That's more or less how build-in `defn`
|
||||
;; macro is implemented.
|
||||
(defmacro define-fn
|
||||
[name & body]
|
||||
`(def ~name (fn ~@body)))
|
||||
|
||||
|
||||
;; Now if we use `define-fn` form above defined macro will be expanded
|
||||
;; and compile time resulting into diff program output.
|
||||
(define-fn print
|
||||
[message]
|
||||
(.log console message))
|
||||
|
||||
|
||||
;; Not all of the macros can be expressed via templating, but all of the
|
||||
;; language is available at hand to assemble macro expanded form.
|
||||
;; For instance let's define macro to ease functional chanining popular
|
||||
;; in JS but usually expressed via method chaining. For example following
|
||||
;; API is pioneered by jQuery is very common in JS:
|
||||
;;
|
||||
;; open(target, "keypress).
|
||||
;; filter(isEnterKey).
|
||||
;; map(getInputText).
|
||||
;; reduce(render)
|
||||
;;
|
||||
;; Unfortunately though it usually requires all the functions need to be
|
||||
;; methods of dsl object, which is very limited. Making third party
|
||||
;; functions second class. Via macros we can achieve similar chaining
|
||||
;; without such tradeoffs.
|
||||
(defmacro ->
|
||||
[& operations]
|
||||
(reduce
|
||||
(fn [form operation]
|
||||
(cons (first operation)
|
||||
(cons form (rest operation))))
|
||||
(first operations)
|
||||
(rest operations)))
|
||||
|
||||
(->
|
||||
(open tagret :keypress)
|
||||
(filter enter-key?)
|
||||
(map get-input-text)
|
||||
(reduce render))
|
||||
Reference in New Issue
Block a user