mirror of
https://github.com/janet-lang/janet
synced 2024-12-23 23:10:26 +00:00
Merge branch 'master' of https://github.com/bakpakin/janet
This commit is contained in:
commit
02673dd791
32
Makefile
32
Makefile
@ -149,18 +149,6 @@ valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
|
|||||||
for f in build/*.out; do $(VALGRIND_COMMAND) "$$f" || exit; done
|
for f in build/*.out; do $(VALGRIND_COMMAND) "$$f" || exit; done
|
||||||
for f in test/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
for f in test/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||||
|
|
||||||
###################
|
|
||||||
##### Natives #####
|
|
||||||
###################
|
|
||||||
|
|
||||||
natives: $(JANET_TARGET)
|
|
||||||
$(MAKE) -C natives/json
|
|
||||||
$(MAKE) -j 8 -C natives/sqlite3
|
|
||||||
|
|
||||||
clean-natives:
|
|
||||||
$(MAKE) -C natives/json clean
|
|
||||||
$(MAKE) -C natives/sqlite3 clean
|
|
||||||
|
|
||||||
########################
|
########################
|
||||||
##### Distribution #####
|
##### Distribution #####
|
||||||
########################
|
########################
|
||||||
@ -168,9 +156,19 @@ clean-natives:
|
|||||||
dist: build/janet-dist.tar.gz
|
dist: build/janet-dist.tar.gz
|
||||||
|
|
||||||
build/janet-%.tar.gz: $(JANET_TARGET) src/include/janet/janet.h \
|
build/janet-%.tar.gz: $(JANET_TARGET) src/include/janet/janet.h \
|
||||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) README.md
|
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) \
|
||||||
|
build/doc.html README.md $(wildcard doc/*)
|
||||||
tar -czvf $@ $^
|
tar -czvf $@ $^
|
||||||
|
|
||||||
|
#########################
|
||||||
|
##### Documentation #####
|
||||||
|
#########################
|
||||||
|
|
||||||
|
docs: build/doc.html
|
||||||
|
|
||||||
|
build/doc.html: $(JANET_TARGET) doc/gendoc.janet
|
||||||
|
$(JANET_TARGET) doc/gendoc.janet > build/doc.html
|
||||||
|
|
||||||
#################
|
#################
|
||||||
##### Other #####
|
##### Other #####
|
||||||
#################
|
#################
|
||||||
@ -189,16 +187,12 @@ install: $(JANET_TARGET)
|
|||||||
mandb
|
mandb
|
||||||
$(LDCONFIG)
|
$(LDCONFIG)
|
||||||
|
|
||||||
install-libs: natives
|
|
||||||
mkdir -p $(JANET_PATH)
|
|
||||||
cp -r lib $(JANET_PATH)
|
|
||||||
cp natives/*/*.so $(JANET_PATH)
|
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
-rm $(BINDIR)/../$(JANET_TARGET)
|
-rm $(BINDIR)/../$(JANET_TARGET)
|
||||||
-rm $(LIBDIR)/../$(JANET_LIBRARY)
|
-rm $(LIBDIR)/../$(JANET_LIBRARY)
|
||||||
-rm -rf $(INCLUDEDIR)
|
-rm -rf $(INCLUDEDIR)
|
||||||
$(LDCONFIG)
|
$(LDCONFIG)
|
||||||
|
|
||||||
.PHONY: clean install repl debug valgrind test valtest emscripten dist install uninstall \
|
.PHONY: clean install repl debug valgrind test \
|
||||||
|
valtest emscripten dist uninstall docs \
|
||||||
$(TEST_PROGRAM_PHONIES) $(TEST_PROGRAM_VALPHONIES)
|
$(TEST_PROGRAM_PHONIES) $(TEST_PROGRAM_VALPHONIES)
|
||||||
|
24
README.md
24
README.md
@ -1,9 +1,9 @@
|
|||||||
# janet
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/bakpakin/janet.svg?branch=master)](https://travis-ci.org/bakpakin/janet)
|
[![Build Status](https://travis-ci.org/bakpakin/janet.svg?branch=master)](https://travis-ci.org/bakpakin/janet)
|
||||||
[![Appveyor Status](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true)](https://ci.appveyor.com/project/bakpakin/janet)
|
[![Appveyor Status](https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true)](https://ci.appveyor.com/project/bakpakin/janet)
|
||||||
|
|
||||||
Janet is a functional and imperative programming language and bytecode interpreter. It is a
|
<img src="https://raw.githubusercontent.com/honix/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">
|
||||||
|
|
||||||
|
**Janet** is a functional and imperative programming language and bytecode interpreter. It is a
|
||||||
modern lisp, but lists are replaced
|
modern lisp, but lists are replaced
|
||||||
by other data structures with better utility and performance (arrays, tables, structs, tuples).
|
by other data structures with better utility and performance (arrays, tables, structs, tuples).
|
||||||
The language also bridging bridging to native code written in C, meta-programming with macros, and bytecode assembly.
|
The language also bridging bridging to native code written in C, meta-programming with macros, and bytecode assembly.
|
||||||
@ -13,12 +13,15 @@ to run script files. This client program is separate from the core runtime, so
|
|||||||
janet could be embedded into other programs. Try janet in your browser at
|
janet could be embedded into other programs. Try janet in your browser at
|
||||||
[https://janet-lang.org](https://janet-lang.org).
|
[https://janet-lang.org](https://janet-lang.org).
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
Implemented in mostly standard C99, janet runs on Windows, Linux and macOS.
|
Implemented in mostly standard C99, janet runs on Windows, Linux and macOS.
|
||||||
The few features that are not standard C (dynamic library loading, compiler specific optimizations),
|
The few features that are not standard C (dynamic library loading, compiler specific optimizations),
|
||||||
are fairly straight forward. Janet can be easily ported to new platforms.
|
are fairly straight forward. Janet can be easily ported to new platforms.
|
||||||
|
|
||||||
For syntax highlighting, there is some preliminary vim syntax highlighting in [janet.vim](https://github.com/bakpakin/janet.vim).
|
For syntax highlighting, there is some preliminary vim syntax highlighting in [janet.vim](https://github.com/bakpakin/janet.vim).
|
||||||
Generic lisp syntax highlighting should, however, provide good results.
|
Generic lisp syntax highlighting should, however, provide good results. There is also a janet.tmLanguage file
|
||||||
|
that should provide good syntax highlighting for many editors.
|
||||||
|
|
||||||
## Use Cases
|
## Use Cases
|
||||||
|
|
||||||
@ -48,11 +51,16 @@ Janet makes a good system scripting language, or a language to embed in other pr
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
API documentation and design documents can be found in the
|
Documentation can be found in the doc directory of
|
||||||
[wiki](https://github.com/bakpakin/janet/wiki). There is an introduction
|
the repository. There is an introduction
|
||||||
section in the wiki that contains a good overview of the language.
|
section contains a good overview of the language.
|
||||||
|
|
||||||
For individual bindings, use the `(doc symbol-name)` macro to get API
|
API documentation for all bindings can also be generated
|
||||||
|
with `make docs`, which will create `build/doc.html`, which
|
||||||
|
can be viewed with any web browser. This
|
||||||
|
includes all forms in the core library except special forms.
|
||||||
|
|
||||||
|
For individual bindings from within the REPL, use the `(doc symbol-name)` macro to get API
|
||||||
documentation for the core library. For example,
|
documentation for the core library. For example,
|
||||||
```
|
```
|
||||||
(doc doc)
|
(doc doc)
|
||||||
|
BIN
assets/janet-big.png
Normal file
BIN
assets/janet-big.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
assets/janet-w200.png
Normal file
BIN
assets/janet-w200.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
@ -43,13 +43,13 @@ mkdir build\mainclient
|
|||||||
|
|
||||||
@rem Build the sources
|
@rem Build the sources
|
||||||
for %%f in (src\core\*.c) do (
|
for %%f in (src\core\*.c) do (
|
||||||
@%JANET_COMPILE% /Fobuild\core\%%~nf.obj %%f
|
@%JANET_COMPILE% /Fobuild\core\%%~nf.obj %%f
|
||||||
@if errorlevel 1 goto :BUILDFAIL
|
@if errorlevel 1 goto :BUILDFAIL
|
||||||
)
|
)
|
||||||
|
|
||||||
@rem Build the main client
|
@rem Build the main client
|
||||||
for %%f in (src\mainclient\*.c) do (
|
for %%f in (src\mainclient\*.c) do (
|
||||||
@%JANET_COMPILE% /Fobuild\mainclient\%%~nf.obj %%f
|
@%JANET_COMPILE% /Fobuild\mainclient\%%~nf.obj %%f
|
||||||
@if errorlevel 1 goto :BUILDFAIL
|
@if errorlevel 1 goto :BUILDFAIL
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ exit /b 1
|
|||||||
@rem Show help
|
@rem Show help
|
||||||
:HELP
|
:HELP
|
||||||
@echo.
|
@echo.
|
||||||
@echo Usage: build_windows [subcommand=clean,help,test]
|
@echo Usage: build_windows [subcommand=clean,help,test,dist]
|
||||||
@echo.
|
@echo.
|
||||||
@echo Script to build janet on windows. Must be run from the Visual Studio
|
@echo Script to build janet on windows. Must be run from the Visual Studio
|
||||||
@echo command prompt.
|
@echo command prompt.
|
||||||
@ -85,20 +85,22 @@ exit /b 0
|
|||||||
@rem Run tests
|
@rem Run tests
|
||||||
:TEST
|
:TEST
|
||||||
for %%f in (test/suite*.janet) do (
|
for %%f in (test/suite*.janet) do (
|
||||||
janet.exe test\%%f
|
janet.exe test\%%f
|
||||||
@if errorlevel 1 goto :TESTFAIL
|
@if errorlevel 1 goto :TESTFAIL
|
||||||
)
|
)
|
||||||
exit /b 0
|
exit /b 0
|
||||||
|
|
||||||
@rem Build a dist directory
|
@rem Build a dist directory
|
||||||
:DIST
|
:DIST
|
||||||
mkdir dist
|
mkdir dist
|
||||||
|
janet.exe doc\gendoc.janet > dist\doc.html
|
||||||
copy janet.exe dist\janet.exe
|
copy janet.exe dist\janet.exe
|
||||||
copy LICENSE dist\LICENSE
|
copy LICENSE dist\LICENSE
|
||||||
copy README.md dist\README.md
|
copy README.md dist\README.md
|
||||||
copy janet.lib dist\janet.lib
|
copy janet.lib dist\janet.lib
|
||||||
copy janet.exp dist\janet.exp
|
copy janet.exp dist\janet.exp
|
||||||
copy src\include\janet\janet.h dist\janet.h
|
copy src\include\janet\janet.h dist\janet.h
|
||||||
|
xcopy /s doc dist\doc
|
||||||
exit /b 0
|
exit /b 0
|
||||||
|
|
||||||
:TESTFAIL
|
:TESTFAIL
|
||||||
|
6
doc/Home.md
Normal file
6
doc/Home.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Janet is a dynamic, lightweight programming language with strong functional
|
||||||
|
capabilities as well as support for imperative programming. It to be used
|
||||||
|
for short lived scripts as well as for building real programs. It can also
|
||||||
|
be extended with native code (C modules) for better performance and interfacing with
|
||||||
|
existing software. Janet takes ideas from Lua, Scheme, Racket, Clojure, Smalltalk, Erlang, Arc, and
|
||||||
|
a whole bunch of other dynamic languages.
|
746
doc/Introduction.md
Normal file
746
doc/Introduction.md
Normal file
@ -0,0 +1,746 @@
|
|||||||
|
# Hello, world!
|
||||||
|
|
||||||
|
Following tradition, a simple Janet program will print "Hello, world!".
|
||||||
|
|
||||||
|
```
|
||||||
|
(print "Hello, world!")
|
||||||
|
```
|
||||||
|
|
||||||
|
Put the following code in a file named `hello.janet`, and run `./janet hello.janet`.
|
||||||
|
The words "Hello, world!" should be printed to the console, and then the program
|
||||||
|
should immediately exit. You now have a working janet program!
|
||||||
|
|
||||||
|
Alternatively, run the program `./janet` without any arguments to enter a REPL,
|
||||||
|
or read eval print loop. This is a mode where Janet functions like a calculator,
|
||||||
|
reading some input from the user, evaluating it, and printing out the result, all
|
||||||
|
in an infinite loop. This is a useful mode for exploring or prototyping in Janet.
|
||||||
|
|
||||||
|
This hello world program is about the simplest program one can write, and consists of only
|
||||||
|
a few pieces of syntax. This first element is the `print` symbol. This is a function
|
||||||
|
that simply prints its arguments to the console. The second argument is the
|
||||||
|
string literal "Hello, world!", which is the one and only argument to the
|
||||||
|
print function. Lastly, the print symbol and the string literal are wrapped
|
||||||
|
in parentheses, forming a tuple. In Janet, parentheses and brackets are interchangeable,
|
||||||
|
brackets are used mostly when the resulting tuple is not a function call. The tuple
|
||||||
|
above indicates that the function `print` is to be called with one argument, `"Hello, world"`.
|
||||||
|
|
||||||
|
Like all lisps, all operations in Janet are in prefix notation; the name of the
|
||||||
|
operator is the first value in the tuple, and the arguments passed to it are
|
||||||
|
in the rest of the tuple.
|
||||||
|
|
||||||
|
# A bit more - Arithmetic
|
||||||
|
|
||||||
|
Any programming language will have some way to do arithmetic. Janet is no exception,
|
||||||
|
and supports the basic arithmetic operators
|
||||||
|
|
||||||
|
```
|
||||||
|
# Prints 13
|
||||||
|
# (1 + (2*2) + (10/5) + 3 + 4 + (5 - 6))
|
||||||
|
(print (+ 1 (* 2 2) (/ 10 5) 3 4 (- 5 6)))
|
||||||
|
```
|
||||||
|
|
||||||
|
Just like the print function, all arithmetic operators are entered in
|
||||||
|
prefix notation. Janet also supports the remainder operator, or `%`, which returns
|
||||||
|
the remainder of division. For example, `(% 10 3)` is 1, and `(% 10.5 3)` is
|
||||||
|
1.5. The lines that begin with `#` are comments.
|
||||||
|
|
||||||
|
Janet actually has two "flavors" of numbers; integers and real numbers. Integers are any
|
||||||
|
integer value between -2,147,483,648 and 2,147,483,647 (32 bit signed integer).
|
||||||
|
Reals are real numbers, and are represented by IEEE-754 double precision floating point
|
||||||
|
numbers. That means that they can represent any number an integer can represent, as well
|
||||||
|
fractions to very high precision.
|
||||||
|
|
||||||
|
Although real numbers can represent any value an integer can, try to distinguish between
|
||||||
|
real numbers and integers in your program. If you are using a number to index into a structure,
|
||||||
|
you probably want integers. Otherwise, you may want to use reals (this is only a rule of thumb).
|
||||||
|
|
||||||
|
Arithmetic operator will convert integers to real numbers if needed, but real numbers
|
||||||
|
will not be converted to integers, as not all real numbers can be safely converted to integers.
|
||||||
|
|
||||||
|
## Numeric literals
|
||||||
|
|
||||||
|
Numeric literals can be written in many ways. Numbers can be written in base 10, with
|
||||||
|
underscores used to separate digits into groups. A decimal point can be used for floating
|
||||||
|
point numbers. Numbers can also be written in other bases by prefixing the number with the desired
|
||||||
|
base and the character 'r'. For example, 16 can be written as `16`, `1_6`, `16r10`, `4r100`, or `0x10`. The
|
||||||
|
`0x` prefix can be used for hexadecimal as it is so common. The radix must be themselves written in base 10, and
|
||||||
|
can be any integer from 2 to 36. For any radix above 10, use the letters as digits (not case sensitive).
|
||||||
|
|
||||||
|
Numbers can also be in scientific notation such as `3e10`. A custom radix can be used as well
|
||||||
|
as for scientific notation numbers, (the exponent will share the radix). For numbers in scientific
|
||||||
|
notation with a radix besides 10, use the `&` symbol to indicate the exponent rather then `e`.
|
||||||
|
|
||||||
|
## Arithmetic Functions
|
||||||
|
|
||||||
|
Besides the 5 main arithmetic functions, janet also supports a number of math functions
|
||||||
|
taken from the C library `<math.h>`, as well as bitwise operators that behave like they
|
||||||
|
do in C or Java. Functions like `math/sin`, `math/cos`, `math/log`, and `math/exp` will
|
||||||
|
behave as expected to a C programmer. They all take either 1 or 2 numeric arguments and
|
||||||
|
return a real number (never an integer!) Bitwise functions are all prefixed with b.
|
||||||
|
Thet are `bnot`, `bor`, `bxor`, `band`, `blshift`, `brshift`, and `brushift`. Bitwise
|
||||||
|
functions only work on integers.
|
||||||
|
|
||||||
|
# Strings, Keywords and Symbols
|
||||||
|
|
||||||
|
Janet supports several varieties of types that can be used as labels for things in
|
||||||
|
your program. The most useful type for this purpose is the keyword type. A keyword
|
||||||
|
begins with a semicolon, and then contains 0 or more alphanumeric or a few other common
|
||||||
|
characters. For example, `:hello`, `:my-name`, `::`, and `:ABC123_-*&^%$` are all keywords.
|
||||||
|
Keywords are actually just special cases of symbols, which are similar but don't start with
|
||||||
|
a semicolon. The difference between symbols and keywords is that keywords evaluate to themselves, while
|
||||||
|
symbols evaluate to whatever they are bound to. To have a symbol evaluate to itself, it must be
|
||||||
|
quoted.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
# Evaluates to :monday
|
||||||
|
:monday
|
||||||
|
|
||||||
|
# Will throw a compile error as monday is not defined
|
||||||
|
monday
|
||||||
|
|
||||||
|
# Quote it - evaluates to the symbol monday
|
||||||
|
'monday
|
||||||
|
|
||||||
|
# Or first define monday
|
||||||
|
(def monday "It is monday")
|
||||||
|
|
||||||
|
# Now the evaluation should work - monday evaluates to "It is monday"
|
||||||
|
monday
|
||||||
|
```
|
||||||
|
|
||||||
|
The most common thing to do with a keyword is to check it for equality or use it as a key into
|
||||||
|
a table or struct. Note that symbols, keywords and strings are all immutable. Besides making your
|
||||||
|
code easier to reason about, it allows for many optimizations involving these types.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
# Evaluates to true
|
||||||
|
(= :hello :hello)
|
||||||
|
|
||||||
|
# Evaluates to false, everything in janet is case sensitive
|
||||||
|
(= :hello :HeLlO)
|
||||||
|
|
||||||
|
# Look up into a table - evaluates to 25
|
||||||
|
(get {
|
||||||
|
:name "John"
|
||||||
|
:age 25
|
||||||
|
:occupation "plumber"
|
||||||
|
} :age)
|
||||||
|
```
|
||||||
|
|
||||||
|
Strings can be used similarly to keywords, but there primary usage is for defining either text
|
||||||
|
or arbitrary sequences of bytes. Strings (and symbols) in janet are what is sometimes known as
|
||||||
|
"8-bit clean"; they can hold any number of bytes, and are completely unaware of things like character
|
||||||
|
encodings. This is completely compatible with ASCII and UTF-8, two of the most common character
|
||||||
|
encodings. By being encoding agnostic, janet strings can be very simple, fast, and useful for
|
||||||
|
for other uses besides holding text.
|
||||||
|
|
||||||
|
Literal text can be entered inside quotes, as we have seen above.
|
||||||
|
|
||||||
|
```
|
||||||
|
"Hello, this is a string."
|
||||||
|
|
||||||
|
# We can also add escape characters for newlines, double quotes, backslash, tabs, etc.
|
||||||
|
"Hello\nThis is on line two\n\tThis is indented\n"
|
||||||
|
|
||||||
|
# For long strings where you don't want to type a lot of escape characters,
|
||||||
|
# you can use 1 or more backticks (`\``) to delimit a string.
|
||||||
|
# To close this string, simply repeat the opening sequence of backticks
|
||||||
|
``
|
||||||
|
This is a string.
|
||||||
|
Line 2
|
||||||
|
Indented
|
||||||
|
"We can just type quotes here", and backslashes \ no problem.
|
||||||
|
``
|
||||||
|
```
|
||||||
|
|
||||||
|
# Functions
|
||||||
|
|
||||||
|
Janet is a functional language - that means that one of the basic building blocks of your
|
||||||
|
program will be defining functions (the other is using data structures). Because janet
|
||||||
|
is a Lisp, functions are values just like numbers or strings - they can be passed around and
|
||||||
|
created as needed.
|
||||||
|
|
||||||
|
Functions can be defined with the `defn` macro, like so:
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defn triangle-area
|
||||||
|
"Calculates the area of a triangle."
|
||||||
|
[base height]
|
||||||
|
(print "calculating area of a triangle...")
|
||||||
|
(* base height 0.5))
|
||||||
|
```
|
||||||
|
|
||||||
|
A function defined with `defn` consists of a name, a number of optional flags for def, and
|
||||||
|
finally a function body. The example above is named triangle-area and takes two parameters named base and height. The body of the function will print a message and then evaluate to the area of the triangle.
|
||||||
|
|
||||||
|
Once a function like the above one is defined, the programmer can use the `triangle-area`
|
||||||
|
function just like any other, say `print` or `+`.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
# Prints "calculating area of a triangle..." and then "25"
|
||||||
|
(print (triangle-area 5 10))
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that when nesting function calls in other function calls like above (a call to triangle-area is
|
||||||
|
nested inside a call to print), the inner function calls are evaluated first. Also, arguments to
|
||||||
|
a function call are evaluated in order, from first argument to last argument).
|
||||||
|
|
||||||
|
Because functions are first-class values like numbers or strings, they can be passed
|
||||||
|
as arguments to other functions as well.
|
||||||
|
|
||||||
|
```
|
||||||
|
(print triangle-area)
|
||||||
|
```
|
||||||
|
|
||||||
|
This prints the location in memory of the function triangle area.
|
||||||
|
|
||||||
|
Functions don't need to have names. The `fn` keyword can be used to introduce function
|
||||||
|
literals without binding them to a symbol.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Evaluates to 40
|
||||||
|
((fn [x y] (+ x x y)) 10 20)
|
||||||
|
# Also evaluates to 40
|
||||||
|
((fn [x y &] (+ x x y)) 10 20)
|
||||||
|
|
||||||
|
# Will throw an error about the wrong arity
|
||||||
|
((fn [x] x) 1 2)
|
||||||
|
# Will not throw an error about the wrong arity
|
||||||
|
((fn [x &] x) 1 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
The first expression creates an anonymous function that adds twice
|
||||||
|
the first argument to the second, and then calls that function with arguments 10 and 20.
|
||||||
|
This will return (10 + 10 + 20) = 40.
|
||||||
|
|
||||||
|
There is a common macro `defn` that can be used for creating functions and immediately binding
|
||||||
|
them to a name. `defn` works as expected at both the top level and inside another form. There is also
|
||||||
|
the corresponding
|
||||||
|
|
||||||
|
Note that putting an ampersand at the end of the argument list inhibits strict arity checking.
|
||||||
|
This means that such a function will accept fewer or more arguments than specified.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defn myfun [x y]
|
||||||
|
(+ x x y))
|
||||||
|
|
||||||
|
# You can think of defn as a shorthand for def and fn together
|
||||||
|
(def myfun-same (fn [x y]
|
||||||
|
(+ x x Y)))
|
||||||
|
|
||||||
|
(myfun 3 4) # -> 10
|
||||||
|
```
|
||||||
|
|
||||||
|
Janet has many macros provided for you (and you can write your own).
|
||||||
|
Macros are just functions that take your source code
|
||||||
|
and transform it into some other source code, usually automating some repetitive pattern for you.
|
||||||
|
|
||||||
|
# Defs and Vars
|
||||||
|
|
||||||
|
Values can be bound to symbols for later use using the keyword `def`. Using undefined
|
||||||
|
symbols will raise an error.
|
||||||
|
|
||||||
|
```
|
||||||
|
(def a 100)
|
||||||
|
(def b (+ 1 a))
|
||||||
|
(def c (+ b b))
|
||||||
|
(def d (- c 100))
|
||||||
|
```
|
||||||
|
|
||||||
|
Bindings created with def have lexical scoping. Also, bindings created with def are immutable; they
|
||||||
|
cannot be changed after definition. For mutable bindings, like variables in other programming
|
||||||
|
languages, use the `var` keyword. The assignment special form `set` can then be used to update
|
||||||
|
a var.
|
||||||
|
|
||||||
|
```
|
||||||
|
(var myvar 1)
|
||||||
|
(print myvar)
|
||||||
|
(set myvar 10)
|
||||||
|
(print myvar)
|
||||||
|
```
|
||||||
|
|
||||||
|
In the global scope, you can use the `:private` option on a def or var to prevent it from
|
||||||
|
being exported to code that imports your current module. You can also add documentation to
|
||||||
|
a function by passing a string the def or var command.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def mydef :private "This will have priavte scope. My doc here." 123)
|
||||||
|
(var myvar "docstring here" 321)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scopes
|
||||||
|
|
||||||
|
Defs and vars (collectively known as bindings) live inside what is called a scope. A scope is
|
||||||
|
simply where the bindings are valid. If a binding is referenced outside of its scope, the compiler
|
||||||
|
will throw an error. Scopes are useful for organizing your bindings and my extension your programs.
|
||||||
|
There are two main ways to create a scope in Janet.
|
||||||
|
|
||||||
|
The first is to use the `do` special form. `do` executes a series of statements in a scope
|
||||||
|
and evaluates to the last statement. Bindings create inside the form do not escape outside
|
||||||
|
of its scope.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def a :outera)
|
||||||
|
|
||||||
|
(do
|
||||||
|
(def a 1)
|
||||||
|
(def b 2)
|
||||||
|
(def c 3)
|
||||||
|
(+ a b c)) # -> 6
|
||||||
|
|
||||||
|
a # -> :outera
|
||||||
|
b # -> compile error: "unknown symbol \"b\""
|
||||||
|
c # -> compile error: "unknown symbol \"c\""
|
||||||
|
```
|
||||||
|
|
||||||
|
Any attempt to reference the bindings from the do form after it has finished
|
||||||
|
executing will fail. Also notice who defining `a` inside the do form did not
|
||||||
|
overwrite the original definition of `a` for the global scope.
|
||||||
|
|
||||||
|
The second way to create a scope is to create a closure.
|
||||||
|
The `fn` special form also introduces a scope just like
|
||||||
|
the `do` special form.
|
||||||
|
|
||||||
|
There is another built in macro, `let`, that does multiple defs at once, and then introduces a scope.
|
||||||
|
`let` is a wrapper around a combination of defs and dos, and is the most "functional" way of
|
||||||
|
creating bindings.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(let [a 1
|
||||||
|
b 2
|
||||||
|
c 3]
|
||||||
|
(+ a b c)) # -> 6
|
||||||
|
```
|
||||||
|
|
||||||
|
The above is equivalent to the example using `do` and `def`.
|
||||||
|
This is the preferable form in most cases,
|
||||||
|
but using do with multiple defs is fine as well.
|
||||||
|
|
||||||
|
# Data Structures
|
||||||
|
|
||||||
|
Once you have a handle on functions and the primitive value types, you may be wondering how
|
||||||
|
to work with collections of things. Janet has a small number of core data structure types
|
||||||
|
that are very versatile. Tables, Structs, Arrays, Tuples, Strings, and Buffers, are the 6 main
|
||||||
|
built in data structure types. These data structures can be arranged in a useful table describing
|
||||||
|
there relationship to each other.
|
||||||
|
|
||||||
|
| | Mutable | Immutable |
|
||||||
|
| ---------- | ------- | --------------- |
|
||||||
|
| Indexed | Array | Tuple |
|
||||||
|
| Dictionary | Table | Struct |
|
||||||
|
| Byteseq | Buffer | String (Symbol) |
|
||||||
|
|
||||||
|
Indexed types are linear lists of elements than can be accessed in constant time with an integer index.
|
||||||
|
Indexed types are backed by a single chunk of memory for fast access, and are indexed from 0 as in C.
|
||||||
|
Dictionary types associate keys with values. The difference between dictionaries and indexed types
|
||||||
|
is that dictionaries are not limited to integer keys. They are backed by a hashtable and also offer
|
||||||
|
constant time lookup (and insertion for the mutable case).
|
||||||
|
Finally, the 'byteseq' abstraction is any type that contains a sequence of bytes. A byteseq associates
|
||||||
|
integer keys (the indices) with integer values between 0 and 255 (the byte values). In this way,
|
||||||
|
they behave much like Arrays and Tuples. However, one cannot put non integer values into a byteseq.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def mytuple (tuple 1 2 3))
|
||||||
|
|
||||||
|
(def myarray @(1 2 3))
|
||||||
|
(def myarray (array 1 2 3))
|
||||||
|
|
||||||
|
(def mystruct {
|
||||||
|
:key "value"
|
||||||
|
:key2 "another"
|
||||||
|
1 2
|
||||||
|
4 3})
|
||||||
|
|
||||||
|
(def another-struct
|
||||||
|
(struct :a 1 :b 2))
|
||||||
|
|
||||||
|
(def my-table @{
|
||||||
|
:a :b
|
||||||
|
:c :d
|
||||||
|
:A :qwerty})
|
||||||
|
(def another-table
|
||||||
|
(table 1 2 3 4))
|
||||||
|
|
||||||
|
(def my-buffer @"thisismutable")
|
||||||
|
(def my-buffer2 @```
|
||||||
|
This is also mutable ":)"
|
||||||
|
```)
|
||||||
|
```
|
||||||
|
|
||||||
|
To read the values in a data structure, use the get function. The first parameter is the data structure
|
||||||
|
itself, and the second parameter is the key.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(get @{:a 1} :a) # -> 1
|
||||||
|
(get {:a 1} :a) # -> 1
|
||||||
|
(get @[:a :b :c] 2) # -> :c
|
||||||
|
(get (tuple "a" "b" "c") 1) # -> "b"
|
||||||
|
(get @"hello, world" 1) # -> 101
|
||||||
|
(get "hello, world" 0) # -> 104
|
||||||
|
```
|
||||||
|
|
||||||
|
### Destructuring
|
||||||
|
|
||||||
|
In many cases, however, you do not need the `get` function at all. Janet supports destructuring, which
|
||||||
|
means both the `def` and `var` special forms can extract values from inside structures themselves.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
# Before, we might do
|
||||||
|
(def my-array @[:mary :had :a :little :lamb])
|
||||||
|
(def lamb (get my-array 4))
|
||||||
|
(print lamb) # Prints :lamb
|
||||||
|
|
||||||
|
# Now, with destructuring,
|
||||||
|
(def [_ _ _ _ lamb] my-array)
|
||||||
|
(print lamb) # Again, prints :lamb
|
||||||
|
|
||||||
|
# Destructuring works with tables as well
|
||||||
|
(def person @{:name "Bob Dylan" :age 77}
|
||||||
|
(def
|
||||||
|
{:name person-name
|
||||||
|
:age person-age} person)
|
||||||
|
```
|
||||||
|
To update a mutable data structure, use the `put` function. It takes 3 arguments, the data structure,
|
||||||
|
the key, and the value, and returns the data structure. The allowed types keys and values
|
||||||
|
depend on what data structure is passed in.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(put @[] 100 :a)
|
||||||
|
(put @{} :key "value")
|
||||||
|
(put @"" 100 92)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that for Arrays and Buffers, putting an index that is outside the length of the data structure
|
||||||
|
will extend the data structure and fill it with nils in the case of the Array,
|
||||||
|
or 0s in the case of the Buffer.
|
||||||
|
|
||||||
|
The last generic function for all data structures is the `length` function. This returns the number of
|
||||||
|
values in a data structure (the number of keys in a dictionary type).
|
||||||
|
|
||||||
|
# Flow Control
|
||||||
|
|
||||||
|
Janet has only two built in primitives to change flow while inside a function. The first is the
|
||||||
|
`if` special form, which behaves as expected in most functional languages. It takes two or three parameters:
|
||||||
|
a condition, an expression to evaluate to if the condition is true (not nil or false),
|
||||||
|
and an optional condition to evaluate to when the condition is nil or false. If the optional parameter
|
||||||
|
is omitted, the if form evaluates to nil.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(if (> 4 3)
|
||||||
|
"4 is greater than 3"
|
||||||
|
"4 is not greater then three") # Evaluates to the first statement
|
||||||
|
|
||||||
|
(if true
|
||||||
|
(print "Hey")) # Will print
|
||||||
|
|
||||||
|
(if false
|
||||||
|
(print "Oy!")) # Will not print
|
||||||
|
```
|
||||||
|
|
||||||
|
The second primitive control flow construct is the while loop. The while behaves much the same
|
||||||
|
as in many other programming languages, including C, Java, and Python. The while loop takes
|
||||||
|
two or more parameters: the first is a condition (like in the `if` statement), that is checked before
|
||||||
|
every iteration of the loop. If it is nil or false, the while loop ends and evaluates to nil. Otherwise,
|
||||||
|
the rest of the parameters will be evaluated sequentially and then the program will return to the beginning
|
||||||
|
of the loop.
|
||||||
|
|
||||||
|
```
|
||||||
|
# Loop from 100 down to 1 and print each time
|
||||||
|
(var i 100)
|
||||||
|
(while (pos? i)
|
||||||
|
(print "the number is " i)
|
||||||
|
(-- i))
|
||||||
|
|
||||||
|
# Print ... until a random number in range [0, 1) is >= 0.9
|
||||||
|
# (math/random evaluates to a value between 0 and 1)
|
||||||
|
(while (> 0.9 (math/random))
|
||||||
|
(print "..."))
|
||||||
|
```
|
||||||
|
|
||||||
|
Besides these special forms, Janet has many macros for both conditional testing and looping
|
||||||
|
that are much better for the majority of cases. For conditional testing, the `cond`, `switch`, and
|
||||||
|
`when` macros can be used to great effect. `cond` can be used for making an if-else chain, where using
|
||||||
|
just raw if forms would result in many parentheses. `case` For looping, the `loop`, `seq`, and `generate`
|
||||||
|
implement janet's form of list comprehension, as in Python or Clojure.
|
||||||
|
|
||||||
|
# The Core Library
|
||||||
|
|
||||||
|
Janet has a built in core library of over 300 functions and macros at the time of writing.
|
||||||
|
While some of these functions may be refactored into separate modules, it is useful to get to know
|
||||||
|
the core to avoid rewriting provided functions.
|
||||||
|
|
||||||
|
For any given function, use the `doc` macro to view the documentation for it in the repl.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(doc defn) -> Prints the documentation for "defn"
|
||||||
|
```
|
||||||
|
To see a list of all global functions in the repl, type the command
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(table/getproto *env*)
|
||||||
|
# Or
|
||||||
|
(all-symbols)
|
||||||
|
```
|
||||||
|
Which will print out every built-in global binding
|
||||||
|
(it will not show your global bindings). To print all
|
||||||
|
of your global bindings, just use \*env\*, which is a var
|
||||||
|
that is bound to the current environment.
|
||||||
|
|
||||||
|
The convention of surrounding a symbol in stars is taken from lisp
|
||||||
|
and Clojure, and indicates a global dynamic variable rather than a normal
|
||||||
|
definition. To get the static environment at the time of compilation, use the
|
||||||
|
`_env` symbol.
|
||||||
|
|
||||||
|
# Prototypes
|
||||||
|
|
||||||
|
To support basic generic programming, Janet tables support a prototype
|
||||||
|
table. A prototype table contains default values for a table if certain keys
|
||||||
|
are not found in the original table. This allows many similar tables to share
|
||||||
|
contents without duplicating memory.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
# One of many Object Oriented schemes that can
|
||||||
|
# be implented in janet.
|
||||||
|
(def proto1 @{:type :custom1
|
||||||
|
:behave (fn [self x] (print "behaving " x))})
|
||||||
|
(def proto2 @{:type :custom2
|
||||||
|
:behave (fn [self x] (print "behaving 2 " x))})
|
||||||
|
|
||||||
|
(def thing1 (table/setproto @{} proto1))
|
||||||
|
(def thing2 (table/setproto @{} proto2))
|
||||||
|
|
||||||
|
(print thing1:type) # prints :custom1
|
||||||
|
(print thing2:type) # prints :custom2
|
||||||
|
|
||||||
|
(thing1:behave thing1 :a) # prints "behaving :a"
|
||||||
|
(thing2:behave thing2 :b) # prints "behaving 2 :b"
|
||||||
|
```
|
||||||
|
|
||||||
|
Looking up in a table with a prototype can be summed up with the following algorithm.
|
||||||
|
|
||||||
|
1. `(get my-table my-key)` is called.
|
||||||
|
2. my-table is checked for the key if my-key. If there is a value for the key, it is returned.
|
||||||
|
3. if there is a prototype table for my-table, set `my-table = my-table's prototype` and got to 2.
|
||||||
|
4. Return nil as the key was not found.
|
||||||
|
|
||||||
|
Janet will check up to about a 1000 prototypes recursively by default before giving up and returning nil. This
|
||||||
|
is to prevent an infinite loop. This value can be changed by adjusting the `JANET_RECURSION_GUARD` value
|
||||||
|
in janet.h.
|
||||||
|
|
||||||
|
Note that Janet prototypes are not as expressive as metatables in Lua and many other languages.
|
||||||
|
This is by design, as adding Lua or Python like capabilities would not be technically difficult.
|
||||||
|
Users should prefer plain data and functions that operate on them rather than mutable objects
|
||||||
|
with methods.
|
||||||
|
|
||||||
|
# Fibers
|
||||||
|
|
||||||
|
Janet has support for single-core asynchronous programming via coroutines, or fibers.
|
||||||
|
Fibers allow a process to stop and resume execution later, essentially enabling
|
||||||
|
multiple returns from a function. This allows many patterns such a schedules, generators,
|
||||||
|
iterators, live debugging, and robust error handling. Janet's error handling is actually built on
|
||||||
|
top of fibers (when an error is thrown, the parent fiber will handle the error).
|
||||||
|
|
||||||
|
A temporary return from a fiber is called a yield, and can be invoked with the `yield` function.
|
||||||
|
To resume a fiber that has been yielded, use the `resume` function. When resume is called on a fiber,
|
||||||
|
it will only return when that fiber either returns, yields, throws an error, or otherwise emits
|
||||||
|
a signal.
|
||||||
|
|
||||||
|
Different from traditional coroutines, Janet's fibers implement a signaling mechanism, which
|
||||||
|
is used to differentiate different kinds of returns. When a fiber yields or throws an error,
|
||||||
|
control is returned to the calling fiber. The parent fiber must then check what kind of state the
|
||||||
|
fiber is in to differentiate errors from return values from user defined signals.
|
||||||
|
|
||||||
|
To create a fiber, user the `fiber/new` function. The fiber constructor take one or two arguments.
|
||||||
|
the first, necessary argument is the function that the fiber will execute. This function must accept
|
||||||
|
an arity of zero. The next optional argument is a collection of flags checking what kinds of
|
||||||
|
signals to trap and return via `resume`. This is useful so
|
||||||
|
the programmer does not need to handle all different kinds of signals from a fiber. Any un-trapped signals
|
||||||
|
are simply propagated to the next fiber.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def f (fiber/new (fn []
|
||||||
|
(yield 1)
|
||||||
|
(yield 2)
|
||||||
|
(yield 3)
|
||||||
|
(yield 4)
|
||||||
|
5)))
|
||||||
|
|
||||||
|
# Get the status of the fiber (:alive, :dead, :debug, :new, :pending, or :user0-:user9)
|
||||||
|
(print (fiber/status f)) # -> :new
|
||||||
|
|
||||||
|
(print (resume f)) # -> prints 1
|
||||||
|
(print (resume f)) # -> prints 2
|
||||||
|
(print (resume f)) # -> prints 3
|
||||||
|
(print (resume f)) # -> prints 4
|
||||||
|
(print (fiber/status f)) # -> print :pending
|
||||||
|
(print (resume f)) # -> prints 5
|
||||||
|
(print (fiber/status f)) # -> print :dead
|
||||||
|
(print (resume f)) # -> throws an error because the fiber is dead
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Fibers to Capture Errors
|
||||||
|
|
||||||
|
Besides being used as coroutines, fibers can be used to implement error handling (exceptions).
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defn my-function-that-errors [x]
|
||||||
|
(print "start function with " x)
|
||||||
|
(error "oops!")
|
||||||
|
(print "never gets here"))
|
||||||
|
|
||||||
|
# Use the :e flag to only trap errors.
|
||||||
|
(def f (fiber/new my-function-that-errors :e))
|
||||||
|
(def result (resume f))
|
||||||
|
(if (= (fiber/status f) :error)
|
||||||
|
(print "result contains the error")
|
||||||
|
(print "result contains the good result"))
|
||||||
|
```
|
||||||
|
|
||||||
|
# Macros
|
||||||
|
|
||||||
|
Janet supports macros like most lisps. A macro is like a function, but transforms
|
||||||
|
the code itself rather than data. They let you extend the syntax of the language itself.
|
||||||
|
|
||||||
|
You have seen some macros already. The `let`, `loop`, and `defn` forms are macros. When the compiler
|
||||||
|
sees a macro, it evaluates the macro and then compiles the result. We say the macro has been
|
||||||
|
*expanded* after the compiler evaluates it. A simple version of the `defn` macro can
|
||||||
|
be thought of as transforming code of the form
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defn1 myfun [x] body)
|
||||||
|
```
|
||||||
|
into
|
||||||
|
```lisp
|
||||||
|
(def myfun (fn myfun [x] body))
|
||||||
|
```
|
||||||
|
|
||||||
|
We could write such a macro like so:
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defmacro defn1 [name args body]
|
||||||
|
(tuple 'def name (tuple 'fn name args body)))
|
||||||
|
```
|
||||||
|
|
||||||
|
There are a couple of issues with this macro, but it will work for simple functions
|
||||||
|
quite well.
|
||||||
|
|
||||||
|
The first issue is that our defn2 macro can't define functions with multiple expressions
|
||||||
|
in the body. We can make the macro variadic, just like a function. Here is a second version
|
||||||
|
of this macro.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defmacro defn2 [name args & body]
|
||||||
|
(tuple 'def name (apply tuple 'fn name args body)))
|
||||||
|
```
|
||||||
|
|
||||||
|
Great! Now we can define functions with multiple elements in the body. We can still improve this
|
||||||
|
macro even more though. First, we can add a docstring to it. If someone is using the function later,
|
||||||
|
they can use `(doc defn3)` to get a description of the function. Next, we can rewrite the macro
|
||||||
|
using janet's builtin quasiquoting facilities.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defmacro defn3
|
||||||
|
"Defines a new function."
|
||||||
|
[name args & body]
|
||||||
|
`(def ,name (fn ,name ,args ,;body)))
|
||||||
|
```
|
||||||
|
|
||||||
|
This is functionally identical to our previous version `defn2`, but written in such
|
||||||
|
a way that the macro output is more clear. The leading backtick is shorthand for the
|
||||||
|
`(quasiquote x)` special form, which is like `(quote x)` except we can unquote
|
||||||
|
expressions inside it. The comma in front of `name` and `args` is an unquote, which
|
||||||
|
allows us to put a value in the quasiquote. Without the unquote, the symbol \'name\'
|
||||||
|
would be put in the returned tuple. Without the unquote, every function we defined
|
||||||
|
would be called \'name\'!.
|
||||||
|
|
||||||
|
Similar to name, we must also unquote body. However, a normal unquote doesn't work.
|
||||||
|
See what happens if we use a normal unquote for body as well.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def name 'myfunction)
|
||||||
|
(def args '[x y z])
|
||||||
|
(defn body '[(print x) (print y) (print z)])
|
||||||
|
|
||||||
|
`(def ,name (fn ,name ,args ,body))
|
||||||
|
# -> (def myfunction (fn myfunction (x y z) ((print x) (print y) (print z))))
|
||||||
|
```
|
||||||
|
|
||||||
|
There is an extra set of parentheses around the body of our function! We don't
|
||||||
|
want to put the body *inside* the form `(fn args ...)`, we want to *splice* it
|
||||||
|
into the form. Luckily, janet has the `(splice x)` special form for this purpose,
|
||||||
|
and a shorthand for it, the ; character.
|
||||||
|
When combined with the unquote special, we get the desired output.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
`(def ,name (fn ,name ,args ,;body))
|
||||||
|
# -> (def myfunction (fn myfunction (x y z) (print x) (print y) (print z)))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hygiene
|
||||||
|
|
||||||
|
Sometime when we write macros, we must generate symbols for local bindings. Ignoring that
|
||||||
|
it could be written as a function, consider
|
||||||
|
the following macro
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defmacro max1
|
||||||
|
"Get the max of two values."
|
||||||
|
[x y]
|
||||||
|
`(if (> ,x ,y) ,x ,y))
|
||||||
|
```
|
||||||
|
|
||||||
|
This almost works, but will evaluate both x and y twice. This is because both show up
|
||||||
|
in the macro twice. For example, `(max1 (do (print 1) 1) (do (print 2) 2))` will
|
||||||
|
print both 1 and 2 twice, which is surprising to a user of this macro.
|
||||||
|
|
||||||
|
We can do better:
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defmacro max2
|
||||||
|
"Get the max of two values."
|
||||||
|
[x y]
|
||||||
|
`(let [x ,x
|
||||||
|
y ,y]
|
||||||
|
(if (> x y) x y)))
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we have no double evaluation problem! But we now have an even more subtle problem.
|
||||||
|
What happens in the following code?
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(def x 10)
|
||||||
|
(max2 8 (+ x 4))
|
||||||
|
```
|
||||||
|
|
||||||
|
We want the max to be 14, but this will actually evaluate to 12! This can be understood
|
||||||
|
if we expand the macro. You can expand macro once in janet using the `(macex1 x)` function.
|
||||||
|
(To expand macros until there are no macros left to expand, use `(macex x)`. Be careful,
|
||||||
|
janet has many macros, so the full expansion may be almost unreadable).
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(macex1 '(max2 8 (+ x 4)))
|
||||||
|
# -> (let (x 8 y (+ x 4)) (if (> x y) x y))
|
||||||
|
```
|
||||||
|
|
||||||
|
After expansion, y wrongly refers to the x inside the macro (which is bound to 8) rather than the x defined
|
||||||
|
to be 10. The problem is the reuse of the symbol x inside the macro, which overshadowed the original
|
||||||
|
binding.
|
||||||
|
|
||||||
|
Janet provides a general solution to this problem in terms of the `(gensym)` function, which returns
|
||||||
|
a symbol which is guarenteed to be unique and not collide with any symbols defined previously. We can define
|
||||||
|
our macro once more for a fully correct macro.
|
||||||
|
|
||||||
|
```lisp
|
||||||
|
(defmacro max3
|
||||||
|
"Get the max of two values."
|
||||||
|
[x y]
|
||||||
|
(def $x (gensym))
|
||||||
|
(def $y (gensym))
|
||||||
|
`(let [,$x ,x
|
||||||
|
,$y ,y]
|
||||||
|
(if (> ,$x ,$y) ,$x ,$y)))
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, macros are very powerful but also are prone to subtle bugs. You must remember that
|
||||||
|
at their core, macros are just functions that output code, and the code that they return must
|
||||||
|
work in many contexts!
|
245
doc/Parser.md
Normal file
245
doc/Parser.md
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
# The Parser
|
||||||
|
|
||||||
|
A Janet program begins life as a text file, just a sequence of byte like
|
||||||
|
any other on your system. Janet source files should be UTF-8 or ASCII
|
||||||
|
encoded. Before Janet can compile or run your program, it must transform
|
||||||
|
your source code into a data structure. Janet is a lisp, which means it is
|
||||||
|
homoiconic - code is data, so all of the facilities in the language for
|
||||||
|
manipulating arrays, tuples, strings, and tables can be used for manipulating
|
||||||
|
your source code as well.
|
||||||
|
|
||||||
|
But before janet code is represented as a data structure, it must be read, or parsed,
|
||||||
|
by the janet parser. Called the reader in many other lisps, the parser is a machine
|
||||||
|
that takes in plain text and outputs data structures which can be used by both
|
||||||
|
the compiler and macros. In janet, it is a parser rather than a reader because
|
||||||
|
there is no code execution at read time. This is safer and simpler, and also
|
||||||
|
lets janet syntax serve as a robust data interchange format. While a parser
|
||||||
|
is not extensible, in janet the philosophy is to extend the language via macros
|
||||||
|
rather than reader macros.
|
||||||
|
|
||||||
|
## Nil, True and False
|
||||||
|
|
||||||
|
Nil, true and false are all literals than can be entered as such
|
||||||
|
in the parser.
|
||||||
|
|
||||||
|
```
|
||||||
|
nil
|
||||||
|
true
|
||||||
|
false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Symbols
|
||||||
|
|
||||||
|
Janet symbols are represented a sequence of alphanumeric characters
|
||||||
|
not starting with a digit. They can also contain the characters
|
||||||
|
\!, @, $, \%, \^, \&, \*, -, \_, +, =, \|, \~, :, \<, \>, ., \?, \\, /, as
|
||||||
|
well as any Unicode codepoint not in the ascii range.
|
||||||
|
|
||||||
|
By convention, most symbols should be all lower case and use dashes to connect words
|
||||||
|
(sometimes called kebab case).
|
||||||
|
|
||||||
|
Symbols that come from another module often contain a forward slash that separates
|
||||||
|
the name of the module from the name of the definition in the module
|
||||||
|
|
||||||
|
```
|
||||||
|
symbol
|
||||||
|
kebab-case-symbol
|
||||||
|
snake_case_symbol
|
||||||
|
my-module/my-fuction
|
||||||
|
*****
|
||||||
|
!%$^*__--__._+++===~-crazy-symbol
|
||||||
|
*global-var*
|
||||||
|
你好
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keywords
|
||||||
|
|
||||||
|
Janet keywords are really just symbols that begin with the character :. However, they
|
||||||
|
are used differently and treated by the compiler as a constant rather than a name for
|
||||||
|
something. Keywords are used mostly for keys in tables and structs, or pieces of syntax
|
||||||
|
in macros.
|
||||||
|
|
||||||
|
```
|
||||||
|
:keyword
|
||||||
|
:range
|
||||||
|
:0x0x0x0
|
||||||
|
:a-keyword
|
||||||
|
::
|
||||||
|
:
|
||||||
|
```
|
||||||
|
|
||||||
|
## Numbers
|
||||||
|
|
||||||
|
Janet numbers are represented by either 32 bit integers or
|
||||||
|
IEEE-754 floating point numbers. The syntax is similar to that of many other languages
|
||||||
|
as well. Numbers can be written in base 10, with
|
||||||
|
underscores used to separate digits into groups. A decimal point can be used for floating
|
||||||
|
point numbers. Numbers can also be written in other bases by prefixing the number with the desired
|
||||||
|
base and the character 'r'. For example, 16 can be written as `16`, `1_6`, `16r10`, `4r100`, or `0x10`. The
|
||||||
|
`0x` prefix can be used for hexadecimal as it is so common. The radix must be themselves written in base 10, and
|
||||||
|
can be any integer from 2 to 36. For any radix above 10, use the letters as digits (not case sensitive).
|
||||||
|
|
||||||
|
```
|
||||||
|
0
|
||||||
|
12
|
||||||
|
-65912
|
||||||
|
4.98
|
||||||
|
1.3e18
|
||||||
|
1.3E18
|
||||||
|
18r123C
|
||||||
|
11raaa&a
|
||||||
|
1_000_000
|
||||||
|
0xbeef
|
||||||
|
```
|
||||||
|
|
||||||
|
## Strings
|
||||||
|
|
||||||
|
Strings in janet are surrounded by double quotes. Strings are 8bit clean, meaning
|
||||||
|
meaning they can contain any arbitrary sequence of bytes, including embedded
|
||||||
|
0s. To insert a double quote into a string itself, escape
|
||||||
|
the double quote with a backslash. For unprintable characters, you can either use
|
||||||
|
one of a few common escapes, use the `\xHH` escape to escape a single byte in
|
||||||
|
hexidecimal. The supported escapes are:
|
||||||
|
|
||||||
|
- \\xHH Escape a single arbitrary byte in hexidecimal.
|
||||||
|
- \\n Newline (ASCII 10)
|
||||||
|
- \\t Tab character (ASCII 9)
|
||||||
|
- \\r Carriage Return (ASCII 13)
|
||||||
|
- \\0 Null (ASCII 0)
|
||||||
|
- \\z Null (ASCII 0)
|
||||||
|
- \\f Form Feed (ASCII 12)
|
||||||
|
- \\e Escape (ASCII 27)
|
||||||
|
- \\" Double Quote (ASCII 34)
|
||||||
|
- \\\\ Backslash (ASCII 92)
|
||||||
|
|
||||||
|
Strings can also contain literal newline characters that will be ignore.
|
||||||
|
This lets one define a multiline string that does not contain newline characters.
|
||||||
|
|
||||||
|
An alternative way of representing strings in janet is the long string, or the backquote
|
||||||
|
delimited string. A string can also be define to start with a certain number of
|
||||||
|
backquotes, and will end the same number of backquotes. Long strings
|
||||||
|
do not contain escape sequences; all bytes will be parsed literally until
|
||||||
|
ending delimiter is found. This is useful
|
||||||
|
for definining multiline strings with literal newline characters, unprintable
|
||||||
|
characters, or strings that would otherwise require many escape sequences.
|
||||||
|
|
||||||
|
```
|
||||||
|
"This is a string."
|
||||||
|
"This\nis\na\nstring."
|
||||||
|
"This
|
||||||
|
is
|
||||||
|
a
|
||||||
|
string."
|
||||||
|
``
|
||||||
|
This
|
||||||
|
is
|
||||||
|
a
|
||||||
|
string
|
||||||
|
``
|
||||||
|
```
|
||||||
|
|
||||||
|
## Buffers
|
||||||
|
|
||||||
|
Buffers are similar strings except they are mutable data structures. Strings in janet
|
||||||
|
cannot be mutated after created, where a buffer can be changed after creation.
|
||||||
|
The syntax for a buffer is the same as that for a string or long string, but
|
||||||
|
the buffer must be prefixed with the '@' character.
|
||||||
|
|
||||||
|
```
|
||||||
|
@""
|
||||||
|
@"Buffer."
|
||||||
|
@``Another buffer``
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tuples
|
||||||
|
|
||||||
|
Tuples are a sequence of white space separated values surrounded by either parentheses
|
||||||
|
or brackets. The parser considers any of the characters ASCII 32, \\0, \\f, \\n, \\r or \\t
|
||||||
|
to be whitespace.
|
||||||
|
|
||||||
|
```
|
||||||
|
(do 1 2 3)
|
||||||
|
[do 1 2 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arrays
|
||||||
|
|
||||||
|
Arrays are the same as tuples, but have a leading @ to indicate mutability.
|
||||||
|
|
||||||
|
```
|
||||||
|
@(:one :two :three)
|
||||||
|
@[:one :two :three]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Structs
|
||||||
|
|
||||||
|
Structs are represented by a sequence of whitespace delimited key value pairs
|
||||||
|
surrounded by curly braces. The sequence is defined as key1, value1, key2, value2, etc.
|
||||||
|
There must be an even number of items between curly braces or the parser will
|
||||||
|
signal a parse error. Any value can be a key or value. Using nil as a key or
|
||||||
|
value, however, will drop that pair from the parsed struct.
|
||||||
|
|
||||||
|
```
|
||||||
|
{}
|
||||||
|
{:key1 "value1" :key2 :value2 :key3 3}
|
||||||
|
{(1 2 3) (4 5 6)}
|
||||||
|
{@[] @[]}
|
||||||
|
{1 2 3 4 5 6}
|
||||||
|
```
|
||||||
|
## Tables
|
||||||
|
|
||||||
|
Table have the same syntax as structs, except they have the @ prefix to indicate
|
||||||
|
that they are mutable.
|
||||||
|
|
||||||
|
```
|
||||||
|
@{}
|
||||||
|
@{:key1 "value1" :key2 :value2 :key3 3}
|
||||||
|
@{(1 2 3) (4 5 6)}
|
||||||
|
@{@[] @[]}
|
||||||
|
@{1 2 3 4 5 6}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
Comments begin with a \# character and continue until the end of the line.
|
||||||
|
There are no multiline comments. For ricm multiline comments, use a
|
||||||
|
string literal.
|
||||||
|
|
||||||
|
## Shorthands
|
||||||
|
|
||||||
|
Often called reader macros in other lisps, Janet provides several shorthand
|
||||||
|
notations for some forms.
|
||||||
|
|
||||||
|
### 'x
|
||||||
|
|
||||||
|
Shorthand for `(quote x)`
|
||||||
|
|
||||||
|
### ;x
|
||||||
|
|
||||||
|
Shorthand for `(splice x)`
|
||||||
|
|
||||||
|
### ~x
|
||||||
|
|
||||||
|
Shorthand for `(quasiquote x)`
|
||||||
|
|
||||||
|
### ,x
|
||||||
|
|
||||||
|
Shorthand for `(unquote x)`
|
||||||
|
|
||||||
|
These shorthand notations can be combined in any order, allowing
|
||||||
|
forms like `''x` (`(quote (quote x))`), or `,;x` (`(unquote (splice x))`).
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
The parser contains the following functions which exposes
|
||||||
|
the parser state machine as a janet abstract object.
|
||||||
|
|
||||||
|
- `parser/byte`
|
||||||
|
- `parser/consume`
|
||||||
|
- `parser/error`
|
||||||
|
- `parser/flush`
|
||||||
|
- `parser/new`
|
||||||
|
- `parser/produce`
|
||||||
|
- `parser/state`
|
||||||
|
- `parser/status`
|
||||||
|
- `parser/where`
|
31
doc/SQLite.md
Normal file
31
doc/SQLite.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# SQLite bindings
|
||||||
|
|
||||||
|
There are some sqlite3 bindings in the directory natives/sqlite3 bundled with
|
||||||
|
the janet source code. They serve mostly as a
|
||||||
|
proof of concept external c library. To use, first compile the module with Make.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make natives
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, enter the repl and create a database and a table.
|
||||||
|
|
||||||
|
```
|
||||||
|
janet:1:> (import natives/sqlite3 :as sql)
|
||||||
|
nil
|
||||||
|
janet:2:> (def db (sql/open "test.db"))
|
||||||
|
<sqlite3.connection 0x5561A138C470>
|
||||||
|
janet:3:> (sql/eval db `CREATE TABLE customers(id INTEGER PRIMARY KEY, name TEXT);`)
|
||||||
|
@[]
|
||||||
|
janet:4:> (sql/eval db `INSERT INTO customers VALUES(:id, :name);` {:name "John" :id 12345})
|
||||||
|
@[]
|
||||||
|
janet:5:> (sql/eval db `SELECT * FROM customers;`)
|
||||||
|
@[{"id" 12345 "name" "John"}]
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, close the database connection when done with it.
|
||||||
|
|
||||||
|
```
|
||||||
|
janet:6:> (sql/close db)
|
||||||
|
nil
|
||||||
|
```
|
238
doc/The-Janet-Abstract-Machine-Bytecode.md
Normal file
238
doc/The-Janet-Abstract-Machine-Bytecode.md
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
The Janet language is implemented on top of an abstract machine (AM). The compiler
|
||||||
|
converts Janet data structures to this bytecode, which can then be efficiently executed
|
||||||
|
from inside a C program. To understand the janet bytecode, it is useful to understand
|
||||||
|
the abstractions used inside the Janet AM, as well as the C types used to implement these
|
||||||
|
features.
|
||||||
|
|
||||||
|
## The Stack = The Fiber
|
||||||
|
|
||||||
|
A Janet Fiber is the type used to represent multiple concurrent processes
|
||||||
|
in janet. It is basically a wrapper around the idea of a stack. The stack is
|
||||||
|
divided into a number of stack frames (`JanetStackFrame *` in C), each of which
|
||||||
|
contains information such as the function that created the stack frame,
|
||||||
|
the program counter for the stack frame, a pointer to the previous frame,
|
||||||
|
and the size of the frame. Each stack frame also is paired with a number
|
||||||
|
registers.
|
||||||
|
|
||||||
|
```
|
||||||
|
X: Slot
|
||||||
|
|
||||||
|
X
|
||||||
|
X - Stack Top, for next function call.
|
||||||
|
-----
|
||||||
|
Frame next
|
||||||
|
-----
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X - Stack 0
|
||||||
|
-----
|
||||||
|
Frame 0
|
||||||
|
-----
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X - Stack -1
|
||||||
|
-----
|
||||||
|
Frame -1
|
||||||
|
-----
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X
|
||||||
|
X - Stack -2
|
||||||
|
-----
|
||||||
|
Frame -2
|
||||||
|
-----
|
||||||
|
...
|
||||||
|
...
|
||||||
|
...
|
||||||
|
-----
|
||||||
|
Bottom of stack
|
||||||
|
```
|
||||||
|
|
||||||
|
Fibers also have an incomplete stack frame for the next function call on top
|
||||||
|
of their stacks. Making a function call involves pushing arguments to this
|
||||||
|
temporary stack, and then invoking either the CALL or TCALL instructions.
|
||||||
|
Arguments for the next function call are pushed via the PUSH, PUSH2, PUSH3, and
|
||||||
|
PUSHA instructions. The stack of a fiber will grow as large as needed, although by
|
||||||
|
default janet will limit the maximum size of a fiber's stack.
|
||||||
|
The maximum stack size can be modified on a per fiber basis.
|
||||||
|
|
||||||
|
The slots in the stack are exposed as virtual registers to instructions. They
|
||||||
|
can hold any Janet value.
|
||||||
|
|
||||||
|
## Closures
|
||||||
|
|
||||||
|
All functions in janet are closures; they combine some bytecode instructions
|
||||||
|
with 0 or more environments. In the C source, a closure (hereby the same as
|
||||||
|
a function) is represented by the type `JanetFunction *`. The bytecode instruction
|
||||||
|
part of the function is represented by `JanetFuncDef *`, and a function environment
|
||||||
|
is represented with `JanetFuncEnv *`.
|
||||||
|
|
||||||
|
The function definition part of a function (the 'bytecode' part, `JanetFuncDef *`),
|
||||||
|
we also store various metadata about the function which is useful for debugging,
|
||||||
|
as well as constants referenced by the function.
|
||||||
|
|
||||||
|
## C Functions
|
||||||
|
|
||||||
|
Janet uses C functions to bridge to native code. A C function
|
||||||
|
(`JanetCFunction *` in C) is a C function pointer that can be called like
|
||||||
|
a normal janet closure. From the perspective of the bytecode instruction set, there is no difference
|
||||||
|
in invoking a C function and invoking a normal janet function.
|
||||||
|
|
||||||
|
## Bytecode Format
|
||||||
|
|
||||||
|
Janet bytecode presents an interface to a virtual machine with a large number
|
||||||
|
of identical registers that can hold any Janet value (`Janet *` in C). Most instructions
|
||||||
|
have a destination register, and 1 or 2 source register. Registers are simply
|
||||||
|
named with positive integers.
|
||||||
|
|
||||||
|
Each instruction is a 32 bit integer, meaning that the instruction set is a constant
|
||||||
|
width RISC instruction set like MIPS. The opcode of each instruction is the least significant
|
||||||
|
byte of the instruction. The highest bit of
|
||||||
|
this leading byte is reserved for debugging purpose, so there are 128 possible opcodes encodable
|
||||||
|
with this scheme. Not all of these possible opcode are defined, and will trap the interpreter
|
||||||
|
and emit a debug signal. Note that this mean an unknown opcode is still valid bytecode, it will
|
||||||
|
just put the interpreter into a debug state when executed.
|
||||||
|
|
||||||
|
```
|
||||||
|
X - Payload bits
|
||||||
|
O - Opcode bits
|
||||||
|
|
||||||
|
4 3 2 1
|
||||||
|
+----+----+----+----+
|
||||||
|
| XX | XX | XX | OO |
|
||||||
|
+----+----+----+----+
|
||||||
|
```
|
||||||
|
|
||||||
|
8 bits for the opcode leaves 24 bits for the payload, which may or may not be utilized.
|
||||||
|
There are a few instruction variants that divide these payload bits.
|
||||||
|
|
||||||
|
* 0 arg - Used for noops, returning nil, or other instructions that take no
|
||||||
|
arguments. The payload is essentially ignored.
|
||||||
|
* 1 arg - All payload bits correspond to a single value, usually a signed or unsigned integer.
|
||||||
|
Used for instructions of 1 argument, like returning a value, yielding a value to the parent fiber,
|
||||||
|
or doing a (relative) jump.
|
||||||
|
* 2 arg - Payload is split into byte 2 and bytes 3 and 4.
|
||||||
|
The first argument is the 8 bit value from byte 2, and the second argument is the 16 bit value
|
||||||
|
from bytes 3 and 4 (`instruction >> 16`). Used for instructions of two arguments, like move, normal
|
||||||
|
function calls, conditionals, etc.
|
||||||
|
* 3 arg - Bytes 2, 3, and 4 each correspond to an 8 bit argument.
|
||||||
|
Used for arithmetic operations, emitting a signal, etc.
|
||||||
|
|
||||||
|
These instruction variants can be further refined based on the semantics of the arguments.
|
||||||
|
Some instructions may treat an argument as a slot index, while other instructions
|
||||||
|
will treat the argument as a signed integer literal, and index for a constant, an index
|
||||||
|
for an environment, or an unsigned integer.
|
||||||
|
|
||||||
|
## Instruction Reference
|
||||||
|
|
||||||
|
A listing of all opcode values can be found in src/include/janet/janetopcodes.h. The janet assembly
|
||||||
|
short names can be found src/assembler/asm.c. In this document, we will refer to the instructions
|
||||||
|
by their short names as presented to the assembler rather than their numerical values.
|
||||||
|
|
||||||
|
Each instruction is also listed with a signature, which are the arguments the instruction
|
||||||
|
expects. There are a handful of instruction signatures, which combine the arity and type
|
||||||
|
of the instruction. The assembler does not
|
||||||
|
do any typechecking per closure, but does prevent jumping to invalid instructions and
|
||||||
|
failure to return or error.
|
||||||
|
|
||||||
|
### Notation
|
||||||
|
|
||||||
|
* The $ prefix indicates that a instruction parameter is acting as a virtual register (slot).
|
||||||
|
If a parameter does not have the $ suffix in the description, it is acting as some kind
|
||||||
|
of literal (usually an unsigned integer for indexes, and a signed integer for literal integers).
|
||||||
|
|
||||||
|
* Some operators in the description have the suffix 'i' or 'r'. These indicate
|
||||||
|
that these operators correspond to integers or real numbers only, respectively. All
|
||||||
|
bitwise operators and bit shifts only work with integers.
|
||||||
|
|
||||||
|
* The `>>>` indicates unsigned right shift, as in Java. Because all integers in janet are
|
||||||
|
signed, we differentiate the two kinds of right bit shift.
|
||||||
|
|
||||||
|
* The 'im' suffix in the instruction name is short for immediate. The 'i' suffix is short for integer,
|
||||||
|
and the 'r' suffix is short for real.
|
||||||
|
|
||||||
|
### Reference Table
|
||||||
|
|
||||||
|
| Instruction | Signature | Description |
|
||||||
|
| ----------- | --------------------------- | --------------------------------- |
|
||||||
|
| `add` | `(add dest lhs rhs)` | $dest = $lhs + $rhs |
|
||||||
|
| `addi` | `(addi dest lhs rhs)` | $dest = $lhs +i $rhs |
|
||||||
|
| `addim` | `(addim dest lhs im)` | $dest = $lhs +i im |
|
||||||
|
| `addr` | `(addr dest lhs rhs)` | $dest = $lhs +r $rhs |
|
||||||
|
| `band` | `(band dest lhs rhs)` | $dest = $lhs & $rhs |
|
||||||
|
| `bnot` | `(bnot dest operand)` | $dest = ~$operand |
|
||||||
|
| `bor` | `(bor dest lhs rhs)` | $dest = $lhs | $rhs |
|
||||||
|
| `bxor` | `(bxor dest lhs rhs)` | $dest = $lhs ^ $rhs |
|
||||||
|
| `call` | `(call dest callee)` | $dest = call($callee, args) |
|
||||||
|
| `clo` | `(clo dest index)` | $dest = closure(defs[$index]) |
|
||||||
|
| `cmp` | `(cmp dest lhs rhs)` | $dest = janet\_compare($lhs, $rhs) |
|
||||||
|
| `div` | `(div dest lhs rhs)` | $dest = $lhs / $rhs |
|
||||||
|
| `divi` | `(divi dest lhs rhs)` | $dest = $lhs /i $rhs |
|
||||||
|
| `divim` | `(divim dest lhs im)` | $dest = $lhs /i im |
|
||||||
|
| `divr` | `(divr dest lhs rhs)` | $dest = $lhs /r $rhs |
|
||||||
|
| `eq` | `(eq dest lhs rhs)` | $dest = $lhs == $rhs |
|
||||||
|
| `eqi` | `(eqi dest lhs rhs)` | $dest = $lhs ==i $rhs |
|
||||||
|
| `eqim` | `(eqim dest lhs im)` | $dest = $lhs ==i im |
|
||||||
|
| `eqr` | `(eqr dest lhs rhs)` | $dest = $lhs ==r $rhs |
|
||||||
|
| `err` | `(err message)` | Throw error $message. |
|
||||||
|
| `get` | `(get dest ds key)` | $dest = $ds[$key] |
|
||||||
|
| `geti` | `(geti dest ds index)` | $dest = $ds[index] |
|
||||||
|
| `gt` | `(gt dest lhs rhs)` | $dest = $lhs > $rhs |
|
||||||
|
| `gti` | `(gti dest lhs rhs)` | $dest = $lhs \>i $rhs |
|
||||||
|
| `gtim` | `(gtim dest lhs im)` | $dest = $lhs \>i im |
|
||||||
|
| `gtr` | `(gtr dest lhs rhs)` | $dest = $lhs \>r $rhs |
|
||||||
|
| `gter` | `(gter dest lhs rhs)` | $dest = $lhs >=r $rhs |
|
||||||
|
| `jmp` | `(jmp label)` | pc = label, pc += offset |
|
||||||
|
| `jmpif` | `(jmpif cond label)` | if $cond pc = label else pc++ |
|
||||||
|
| `jmpno` | `(jmpno cond label)` | if $cond pc++ else pc = label |
|
||||||
|
| `ldc` | `(ldc dest index)` | $dest = constants[index] |
|
||||||
|
| `ldf` | `(ldf dest)` | $dest = false |
|
||||||
|
| `ldi` | `(ldi dest integer)` | $dest = integer |
|
||||||
|
| `ldn` | `(ldn dest)` | $dest = nil |
|
||||||
|
| `lds` | `(lds dest)` | $dest = current closure (self) |
|
||||||
|
| `ldt` | `(ldt dest)` | $dest = true |
|
||||||
|
| `ldu` | `(ldu dest env index)` | $dest = envs[env][index] |
|
||||||
|
| `len` | `(len dest ds)` | $dest = length(ds) |
|
||||||
|
| `lt` | `(lt dest lhs rhs)` | $dest = $lhs < $rhs |
|
||||||
|
| `lti` | `(lti dest lhs rhs)` | $dest = $lhs \<i $rhs |
|
||||||
|
| `ltim` | `(ltim dest lhs im)` | $dest = $lhs \<i im |
|
||||||
|
| `ltr` | `(ltr dest lhs rhs)` | $dest = $lhs \<r $rhs |
|
||||||
|
| `mkarr` | `(mkarr dest)` | $dest = call(array, args) |
|
||||||
|
| `mkbuf` | `(mkbuf dest)` | $dest = call(buffer, args) |
|
||||||
|
| `mktab` | `(mktab dest)` | $dest = call(table, args) |
|
||||||
|
| `mkstr` | `(mkstr dest)` | $dest = call(string, args) |
|
||||||
|
| `mkstu` | `(mkstu dest)` | $dest = call(struct, args) |
|
||||||
|
| `mktup` | `(mktup dest)` | $dest = call(tuple, args) |
|
||||||
|
| `movf` | `(movf src dest)` | $dest = $src |
|
||||||
|
| `movn` | `(movn dest src)` | $dest = $src |
|
||||||
|
| `mul` | `(mul dest lhs rhs)` | $dest = $lhs * $rhs |
|
||||||
|
| `muli` | `(muli dest lhs rhs)` | $dest = $lhs \*i $rhs |
|
||||||
|
| `mulim` | `(mulim dest lhs im)` | $dest = $lhs \*i im |
|
||||||
|
| `mulr` | `(mulr dest lhs rhs)` | $dest = $lhs \*r $rhs |
|
||||||
|
| `noop` | `(noop)` | Does nothing. |
|
||||||
|
| `push` | `(push val)` | Push $val on arg |
|
||||||
|
| `push2` | `(push2 val1 val3)` | Push $val1, $val2 on args |
|
||||||
|
| `push3` | `(push3 val1 val2 val3)` | Push $val1, $val2, $val3, on args |
|
||||||
|
| `pusha` | `(pusha array)` | Push values in $array on args |
|
||||||
|
| `put` | `(put ds key val)` | $ds[$key] = $val |
|
||||||
|
| `puti` | `(puti ds index val)` | $ds[index] = $val |
|
||||||
|
| `res` | `(res dest fiber val)` | $dest = resume $fiber with $val |
|
||||||
|
| `ret` | `(ret val)` | Return $val |
|
||||||
|
| `retn` | `(retn)` | Return nil |
|
||||||
|
| `setu` | `(setu env index val)` | envs[env][index] = $val |
|
||||||
|
| `sig` | `(sig dest value sigtype)` | $dest = emit $value as sigtype |
|
||||||
|
| `sl` | `(sl dest lhs rhs)` | $dest = $lhs << $rhs |
|
||||||
|
| `slim` | `(slim dest lhs shamt)` | $dest = $lhs << shamt |
|
||||||
|
| `sr` | `(sr dest lhs rhs)` | $dest = $lhs >> $rhs |
|
||||||
|
| `srim` | `(srim dest lhs shamt)` | $dest = $lhs >> shamt |
|
||||||
|
| `sru` | `(sru dest lhs rhs)` | $dest = $lhs >>> $rhs |
|
||||||
|
| `sruim` | `(sruim dest lhs shamt)` | $dest = $lhs >>> shamt |
|
||||||
|
| `sub` | `(sub dest lhs rhs)` | $dest = $lhs - $rhs |
|
||||||
|
| `tcall` | `(tcall callee)` | Return call($callee, args) |
|
||||||
|
| `tchck` | `(tcheck slot types)` | Assert $slot does matches types |
|
||||||
|
|
97
doc/gendoc.janet
Normal file
97
doc/gendoc.janet
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Generate documentation
|
||||||
|
|
||||||
|
(def- prelude
|
||||||
|
```
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Janet Language Documentation</title>
|
||||||
|
<meta name="description" content="API Documentation for the janet programming language.">
|
||||||
|
<style>
|
||||||
|
.docstring {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
.binding-type {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
.source-map {
|
||||||
|
color: steelblue;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
```)
|
||||||
|
|
||||||
|
(def- postlude
|
||||||
|
```
|
||||||
|
</html>
|
||||||
|
```)
|
||||||
|
|
||||||
|
(def- escapes
|
||||||
|
{10 "<br>"
|
||||||
|
09 " "
|
||||||
|
38 "&"
|
||||||
|
60 "<"
|
||||||
|
62 ">"
|
||||||
|
34 """
|
||||||
|
39 "'"
|
||||||
|
47 "/"})
|
||||||
|
|
||||||
|
(defn- trim-lead
|
||||||
|
"Trim leading newlines"
|
||||||
|
[str]
|
||||||
|
(var i 0)
|
||||||
|
(while (= 10 str.i) (++ i))
|
||||||
|
(string/slice str i))
|
||||||
|
|
||||||
|
(defn- html-escape
|
||||||
|
"Escape special characters for HTML encoding."
|
||||||
|
[str]
|
||||||
|
(def buf @"")
|
||||||
|
(loop [byte :in str]
|
||||||
|
(if-let [rep escapes.byte]
|
||||||
|
(buffer/push-string buf rep)
|
||||||
|
(buffer/push-byte buf byte)))
|
||||||
|
buf)
|
||||||
|
|
||||||
|
(defn- make-title
|
||||||
|
"Generate title"
|
||||||
|
[]
|
||||||
|
(string "<h1>Janet Core API</h1>"
|
||||||
|
"<p>Version " janet/version "-" janet/build "</p>"
|
||||||
|
"<p>Generated "
|
||||||
|
(string/number (os/time) :f 0 20)
|
||||||
|
" seconds after epoch</p>"
|
||||||
|
"<hr>"))
|
||||||
|
|
||||||
|
(defn- emit-item
|
||||||
|
"Generate documentation for one entry."
|
||||||
|
[key env-entry]
|
||||||
|
(let [{:macro macro
|
||||||
|
:value val
|
||||||
|
:ref ref
|
||||||
|
:source-map sm
|
||||||
|
:doc docstring} env-entry
|
||||||
|
binding-type (cond
|
||||||
|
macro :macro
|
||||||
|
ref (string :var " (" (type ref.0) ")")
|
||||||
|
(type val))
|
||||||
|
source-ref (if-let [[path start end] sm]
|
||||||
|
(string "<span class=\"source-map\">" path " (" start ":" end ")</span>")
|
||||||
|
"")]
|
||||||
|
(string "<h2 class=\"binding\">" (html-escape key) "</h2>\n"
|
||||||
|
"<span class=\"binding-type\">" binding-type "</span>\n"
|
||||||
|
"<p class=\"docstring\">" (trim-lead (html-escape docstring)) "</p>\n"
|
||||||
|
source-ref)))
|
||||||
|
|
||||||
|
# Generate parts and print them to stdout
|
||||||
|
(def parts (seq [[k entry]
|
||||||
|
:in (sort (pairs (table/getproto _env)))
|
||||||
|
:when (and entry:doc (not entry:private))]
|
||||||
|
(emit-item k entry)))
|
||||||
|
(print
|
||||||
|
prelude
|
||||||
|
(make-title)
|
||||||
|
;(interpose "<hr>\n" parts)
|
||||||
|
postlude)
|
@ -18,12 +18,12 @@
|
|||||||
(if ,loaded
|
(if ,loaded
|
||||||
,state
|
,state
|
||||||
(do
|
(do
|
||||||
(:= ,loaded true)
|
(set ,loaded true)
|
||||||
(:= ,state (do ;forms)))))))
|
(set ,state (do ;forms)))))))
|
||||||
|
|
||||||
# Use tuples instead of structs to save memory
|
# Use tuples instead of structs to save memory
|
||||||
(def HEAD :private 0)
|
(def- HEAD 0)
|
||||||
(def TAIL :private 1)
|
(def- TAIL 1)
|
||||||
|
|
||||||
(defn empty-seq
|
(defn empty-seq
|
||||||
"The empty sequence."
|
"The empty sequence."
|
@ -16,7 +16,7 @@
|
|||||||
(def cell-set (frequencies state))
|
(def cell-set (frequencies state))
|
||||||
(def neighbor-set (frequencies (mapcat neighbors state)))
|
(def neighbor-set (frequencies (mapcat neighbors state)))
|
||||||
(seq [coord :keys neighbor-set
|
(seq [coord :keys neighbor-set
|
||||||
:let [count neighbor-set@coord]
|
:let [count neighbor-set.coord]
|
||||||
:when (or (= count 3) (and (get cell-set coord) (= count 2)))]
|
:when (or (= count 3) (and (get cell-set coord) (= count 2)))]
|
||||||
coord))
|
coord))
|
||||||
|
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"Draw cells in the game of life from (x1, y1) to (x2, y2)"
|
"Draw cells in the game of life from (x1, y1) to (x2, y2)"
|
||||||
[state x1 y1 x2 y2]
|
[state x1 y1 x2 y2]
|
||||||
(def cellset @{})
|
(def cellset @{})
|
||||||
(each cell state (:= cellset@cell true))
|
(each cell state (set cellset.cell true))
|
||||||
(loop [x :range [x1 (+ 1 x2)]
|
(loop [x :range [x1 (+ 1 x2)]
|
||||||
:after (print)
|
:after (print)
|
||||||
y :range [y1 (+ 1 y2)]]
|
y :range [y1 (+ 1 y2)]]
|
||||||
@ -40,4 +40,4 @@
|
|||||||
(for i 0 20
|
(for i 0 20
|
||||||
(print "generation " i)
|
(print "generation " i)
|
||||||
(draw *state* -7 -7 7 7)
|
(draw *state* -7 -7 7 7)
|
||||||
(:= *state* (tick *state*)))
|
(set *state* (tick *state*)))
|
||||||
|
@ -9,6 +9,6 @@
|
|||||||
(def len (length list))
|
(def len (length list))
|
||||||
(for j 0 len
|
(for j 0 len
|
||||||
(def trial (get list j))
|
(def trial (get list j))
|
||||||
(if (zero? (% i trial)) (:= isprime? false)))
|
(if (zero? (% i trial)) (set isprime? false)))
|
||||||
(if isprime? (array/push list i)))
|
(if isprime? (array/push list i)))
|
||||||
list)
|
list)
|
||||||
|
335
grammar/janet.tmLanguage
Normal file
335
grammar/janet.tmLanguage
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>fileTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>janet</string>
|
||||||
|
</array>
|
||||||
|
<key>foldingStartMarker</key>
|
||||||
|
<string>\{</string>
|
||||||
|
<key>foldingStopMarker</key>
|
||||||
|
<string>\}</string>
|
||||||
|
<key>foldingStartMarker</key>
|
||||||
|
<string>\[</string>
|
||||||
|
<key>foldingStopMarker</key>
|
||||||
|
<string>\]</string>
|
||||||
|
<key>foldingStartMarker</key>
|
||||||
|
<string>\(</string>
|
||||||
|
<key>foldingStopMarker</key>
|
||||||
|
<string>\)</string>
|
||||||
|
<key>keyEquivalent</key>
|
||||||
|
<string>^~L</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>Janet</string>
|
||||||
|
<key>patterns</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#all</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>repository</key>
|
||||||
|
<dict>
|
||||||
|
<key>all</key>
|
||||||
|
<dict>
|
||||||
|
<key>patterns</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#comment</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#parens</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#brackets</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#braces</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#readermac</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#string</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#longstring</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#literal</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#corelib</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#r-number</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#dec-number</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#hex-number</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#keysym</string>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#symbol</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<key>comment</key>
|
||||||
|
<dict>
|
||||||
|
<key>captures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.comment.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(#).*$</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>comment.line.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>braces</key>
|
||||||
|
<dict>
|
||||||
|
<key>begin</key>
|
||||||
|
<string>(@?{)</string>
|
||||||
|
<key>captures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.braces.begin.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>end</key>
|
||||||
|
<string>(})</string>
|
||||||
|
<key>captures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.braces.end.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>patterns</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#all</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<key>brackets</key>
|
||||||
|
<dict>
|
||||||
|
<key>begin</key>
|
||||||
|
<string>(@?\[)</string>
|
||||||
|
<key>captures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.brackets.begin.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>end</key>
|
||||||
|
<string>(\])</string>
|
||||||
|
<key>captures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.brackets.end.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>patterns</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#all</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<key>parens</key>
|
||||||
|
<dict>
|
||||||
|
<key>begin</key>
|
||||||
|
<string>(@?\()</string>
|
||||||
|
<key>captures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.parens.begin.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>end</key>
|
||||||
|
<string>(\))</string>
|
||||||
|
<key>captures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.parens.end.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>patterns</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>include</key>
|
||||||
|
<string>#all</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<key>readermac</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>[\'\~\;\,]</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.other.janet</string>
|
||||||
|
</dict>
|
||||||
|
<!-- string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*]) token match here (?![\.:\w_\-=!@\$%^&?|\\/<>*])</string -->
|
||||||
|
<key>literal</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*])(true|false|nil)(?![\.:\w_\-=!@\$%^&?|\\/<>*])</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>constant.language.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>corelib</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*])(def|do|fn|if|quasiquote|quote|set|splice|unquote|var|while|%|%=|\*|\*=|\*doc\-width\*|\*env\*|\+|\+\+|\+=|\-|\-\-|\-=|\->|\->>|/|/=|<|<=|=|==|>|>=|_env|abstract\?|all|all\-symbols|allsyms|and|apply|array|array/concat|array/ensure|array/insert|array/new|array/peek|array/pop|array/push|array/slice|array\?|asm|band|blshift|bnot|boolean\?|bor|brshift|brushift|buffer|buffer/clear|buffer/new|buffer/popn|buffer/push\-byte|buffer/push\-integer|buffer/push\-string|buffer/slice|buffer\?|bxor|bytes\?|callable\?|case|cfunction\?|comment|comp|compile|complement|cond|coro|count|debug|debug/arg\-stack|debug/break|debug/fbreak|debug/lineage|debug/stack|debug/unbreak|debug/unfbreak|dec|deep\-not=|deep=|def\-|default|defglobal|defmacro|defmacro\-|defn|defn\-|describe|dictionary\?|disasm|distinct|doc|doc\*|doc\-format|drop\-until|drop\-while|each|empty\?|env\-lookup|error|eval|eval\-string|even\?|every\?|extreme|false\?|fiber/current|fiber/maxstack|fiber/new|fiber/setmaxstack|fiber/status|fiber\?|file/close|file/flush|file/open|file/popen|file/read|file/seek|file/write|filter|find|find\-index|first|flatten|flatten\-into|for|frequencies|function\?|gccollect|gcinterval|gcsetinterval|generate|gensym|get|getline|hash|idempotent\?|identity|if\-let|if\-not|import|import\*|inc|indexed\?|int|integer\?|interleave|interpose|invert|janet/build|janet/version|juxt|juxt\*|keep|keys|keyword\?|kvs|last|length|let|loop|macex|macex1|make\-env|map|mapcat|marshal|match|match\-1|math/acos|math/asin|math/atan|math/ceil|math/cos|math/e|math/exp|math/floor|math/inf|math/log|math/log10|math/pi|math/pow|math/random|math/seedrandom|math/sin|math/sqrt|math/tan|max|max\-order|merge|merge\-into|min|min\-order|module/find|module/native\-paths|module/paths|native|neg\?|next|nil\?|not|not=|not==|number\?|odd\?|one\?|or|order<|order<=|order>|order>=|os/clock|os/cwd|os/execute|os/exit|os/getenv|os/setenv|os/shell|os/sleep|os/time|os/which|pairs|parser/byte|parser/consume|parser/error|parser/flush|parser/new|parser/produce|parser/state|parser/status|parser/where|partial|pos\?|print|process/args|product|put|range|real|real\?|reduce|repl|require|resume|reverse|run\-context|scan\-integer|scan\-number|scan\-real|sentinel|seq|some|sort|sorted|status\-pp|stderr|stdin|stdout|string|string/ascii\-lower|string/ascii\-upper|string/bytes|string/check\-set|string/find|string/find\-all|string/from\-bytes|string/join|string/number|string/pretty|string/repeat|string/replace|string/replace\-all|string/reverse|string/slice|string/split|string\?|struct|struct\?|sum|symbol|symbol\?|table|table/getproto|table/new|table/rawget|table/setproto|table/to\-struct|table\?|take\-until|take\-while|true\?|tuple|tuple/append|tuple/prepend|tuple/slice|tuple\?|type|unless|unmarshal|update|values|varglobal|when|when\-let|with\-idemp|yield|zero\?|zipcoll)(?![\.:\w_\-=!@\$%^&?|\\/<>*])</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>keyword.control.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>keysym</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*]):[\.:\w_\-=!@\$%^&?|\\/<>*]*</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>constant.keyword.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>symbol</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*])[\.a-zA-Z_\-=!@\$%^&?|\\/<>*][\.:\w_\-=!@\$%^&?|\\/<>*]*</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>variable.other.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>hex-number</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*])[-+]?0x([_\da-fA-F]+|[_\da-fA-F]+\.[_\da-fA-F]*|\.[_\da-fA-F]+)(&[+-]?[\da-fA-F]+)?(?![\.:\w_\-=!@\$%^&?|\\/<>*])</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>constant.numeric.hex.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>dec-number</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*])[-+]?([_\d]+|[_\d]+\.[_\d]*|\.[_\d]+)([eE&][+-]?[\d]+)?(?![\.:\w_\-=!@\$%^&?|\\/<>*])</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>constant.numeric.decimal.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>r-number</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(?<![\.:\w_\-=!@\$%^&?|\\/<>*])[-+]?\d\d?r([_\w]+|[_\w]+\.[_\w]*|\.[_\w]+)(&[+-]?[\w]+)?(?![\.:\w_\-=!@\$%^&?|\\/<>*])</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>constant.numeric.decimal.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>string</key>
|
||||||
|
<dict>
|
||||||
|
<key>begin</key>
|
||||||
|
<string>(@?")</string>
|
||||||
|
<key>beginCaptures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.string.begin.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>end</key>
|
||||||
|
<string>(")</string>
|
||||||
|
<key>endCaptures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.string.end.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>string.quoted.double.janet</string>
|
||||||
|
<key>patterns</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>(\\[ne0zft"\\']|\\x[0-9a-fA-F][0-9a-fA-f])</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>constant.character.escape.janet</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<key>longstring</key>
|
||||||
|
<dict>
|
||||||
|
<key>begin</key>
|
||||||
|
<string>(@?)(`+)</string>
|
||||||
|
<key>beginCaptures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.string.begin.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>2</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.string.begin.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>end</key>
|
||||||
|
<string>\2</string>
|
||||||
|
<key>endCaptures</key>
|
||||||
|
<dict>
|
||||||
|
<key>1</key>
|
||||||
|
<dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>punctuation.definition.string.end.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>name</key>
|
||||||
|
<string>string.quoted.triple.janet</string>
|
||||||
|
</dict>
|
||||||
|
<key>nomatch</key>
|
||||||
|
<dict>
|
||||||
|
<key>match</key>
|
||||||
|
<string>\S+</string>
|
||||||
|
<key>name</key>
|
||||||
|
<string>invalid.illegal.janet</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>scopeName</key>
|
||||||
|
<string>source.janet</string>
|
||||||
|
<key>uuid</key>
|
||||||
|
<string>3743190f-20c4-44d0-8640-6611a983296b</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
30
grammar/tmcorelib.janet
Normal file
30
grammar/tmcorelib.janet
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Helper to generate core library mappings for janet
|
||||||
|
|
||||||
|
(def allsyms (all-symbols))
|
||||||
|
|
||||||
|
(def- escapes
|
||||||
|
{(get "|" 0) `\|`
|
||||||
|
(get "-" 0) `\-`
|
||||||
|
(get "+" 0) `\+`
|
||||||
|
(get "*" 0) `\*`
|
||||||
|
(get "^" 0) `\^`
|
||||||
|
(get "$" 0) `\$`
|
||||||
|
(get "?" 0) `\?`
|
||||||
|
38 "&"
|
||||||
|
60 "<"
|
||||||
|
62 ">"
|
||||||
|
34 """
|
||||||
|
39 "'"
|
||||||
|
47 "/"})
|
||||||
|
|
||||||
|
(defn- escape
|
||||||
|
"Escape special characters for HTML and regex encoding."
|
||||||
|
[str]
|
||||||
|
(def buf @"")
|
||||||
|
(loop [byte :in str]
|
||||||
|
(if-let [rep escapes.byte]
|
||||||
|
(buffer/push-string buf rep)
|
||||||
|
(buffer/push-byte buf byte)))
|
||||||
|
buf)
|
||||||
|
|
||||||
|
(print (string/join (map escape allsyms) "|"))
|
@ -588,7 +588,7 @@ static int json_encode(JanetArgs args) {
|
|||||||
|
|
||||||
static const JanetReg cfuns[] = {
|
static const JanetReg cfuns[] = {
|
||||||
{"encode", json_encode,
|
{"encode", json_encode,
|
||||||
"(json/encode x)\n\n"
|
"(json/encode x [,tab [,newline]])\n\n"
|
||||||
"Encodes a janet value in JSON (utf-8)."
|
"Encodes a janet value in JSON (utf-8)."
|
||||||
},
|
},
|
||||||
{"decode", json_decode,
|
{"decode", json_decode,
|
||||||
|
@ -710,8 +710,8 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
|||||||
if (!janet_checktype(tup[1], JANET_INTEGER)) {
|
if (!janet_checktype(tup[1], JANET_INTEGER)) {
|
||||||
janet_asm_error(&a, "expected integer");
|
janet_asm_error(&a, "expected integer");
|
||||||
}
|
}
|
||||||
mapping.line = janet_unwrap_integer(tup[0]);
|
mapping.start = janet_unwrap_integer(tup[0]);
|
||||||
mapping.column = janet_unwrap_integer(tup[1]);
|
mapping.end = janet_unwrap_integer(tup[1]);
|
||||||
def->sourcemap[i] = mapping;
|
def->sourcemap[i] = mapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -876,8 +876,8 @@ Janet janet_disasm(JanetFuncDef *def) {
|
|||||||
for (i = 0; i < def->bytecode_length; i++) {
|
for (i = 0; i < def->bytecode_length; i++) {
|
||||||
Janet *t = janet_tuple_begin(2);
|
Janet *t = janet_tuple_begin(2);
|
||||||
JanetSourceMapping mapping = def->sourcemap[i];
|
JanetSourceMapping mapping = def->sourcemap[i];
|
||||||
t[0] = janet_wrap_integer(mapping.line);
|
t[0] = janet_wrap_integer(mapping.start);
|
||||||
t[1] = janet_wrap_integer(mapping.column);
|
t[1] = janet_wrap_integer(mapping.end);
|
||||||
sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t));
|
sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t));
|
||||||
}
|
}
|
||||||
sourcemap->count = def->bytecode_length;
|
sourcemap->count = def->bytecode_length;
|
||||||
|
@ -125,10 +125,10 @@ int32_t janet_verify(JanetFuncDef *def) {
|
|||||||
for (i = 0; i < def->bytecode_length; i++) {
|
for (i = 0; i < def->bytecode_length; i++) {
|
||||||
uint32_t instr = def->bytecode[i];
|
uint32_t instr = def->bytecode[i];
|
||||||
/* Check for invalid instructions */
|
/* Check for invalid instructions */
|
||||||
if ((instr & 0xFF) >= JOP_INSTRUCTION_COUNT) {
|
if ((instr & 0x7F) >= JOP_INSTRUCTION_COUNT) {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
enum JanetInstructionType type = janet_instructions[instr & 0xFF];
|
enum JanetInstructionType type = janet_instructions[instr & 0x7F];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case JINT_0:
|
case JINT_0:
|
||||||
continue;
|
continue;
|
||||||
|
@ -462,9 +462,9 @@ static int macroexpand1(
|
|||||||
if (janet_tuple_length(form) == 0)
|
if (janet_tuple_length(form) == 0)
|
||||||
return 0;
|
return 0;
|
||||||
/* Source map - only set when we get a tuple */
|
/* Source map - only set when we get a tuple */
|
||||||
if (janet_tuple_sm_line(form) > 0) {
|
if (janet_tuple_sm_start(form) >= 0) {
|
||||||
c->current_mapping.line = janet_tuple_sm_line(form);
|
c->current_mapping.start = janet_tuple_sm_start(form);
|
||||||
c->current_mapping.column = janet_tuple_sm_col(form);
|
c->current_mapping.end = janet_tuple_sm_end(form);
|
||||||
}
|
}
|
||||||
if (!janet_checktype(form[0], JANET_SYMBOL))
|
if (!janet_checktype(form[0], JANET_SYMBOL))
|
||||||
return 0;
|
return 0;
|
||||||
@ -575,13 +575,13 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
|
|||||||
|
|
||||||
if (c->result.status == JANET_COMPILE_ERROR)
|
if (c->result.status == JANET_COMPILE_ERROR)
|
||||||
return janetc_cslot(janet_wrap_nil());
|
return janetc_cslot(janet_wrap_nil());
|
||||||
c->current_mapping = last_mapping;
|
|
||||||
if (opts.flags & JANET_FOPTS_TAIL)
|
if (opts.flags & JANET_FOPTS_TAIL)
|
||||||
ret = janetc_return(opts.compiler, ret);
|
ret = janetc_return(opts.compiler, ret);
|
||||||
if (opts.flags & JANET_FOPTS_HINT) {
|
if (opts.flags & JANET_FOPTS_HINT) {
|
||||||
janetc_copy(opts.compiler, opts.hint, ret);
|
janetc_copy(opts.compiler, opts.hint, ret);
|
||||||
ret = opts.hint;
|
ret = opts.hint;
|
||||||
}
|
}
|
||||||
|
c->current_mapping = last_mapping;
|
||||||
opts.compiler->recursion_guard++;
|
opts.compiler->recursion_guard++;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -648,15 +648,15 @@ static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where)
|
|||||||
c->recursion_guard = JANET_RECURSION_GUARD;
|
c->recursion_guard = JANET_RECURSION_GUARD;
|
||||||
c->env = env;
|
c->env = env;
|
||||||
c->source = where;
|
c->source = where;
|
||||||
c->current_mapping.line = 0;
|
c->current_mapping.start = -1;
|
||||||
c->current_mapping.column = 0;
|
c->current_mapping.end = -1;
|
||||||
/* Init result */
|
/* Init result */
|
||||||
c->result.error = NULL;
|
c->result.error = NULL;
|
||||||
c->result.status = JANET_COMPILE_OK;
|
c->result.status = JANET_COMPILE_OK;
|
||||||
c->result.funcdef = NULL;
|
c->result.funcdef = NULL;
|
||||||
c->result.macrofiber = NULL;
|
c->result.macrofiber = NULL;
|
||||||
c->result.error_mapping.line = 0;
|
c->result.error_mapping.start = -1;
|
||||||
c->result.error_mapping.column = 0;
|
c->result.error_mapping.end = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deinitialize a compiler struct */
|
/* Deinitialize a compiler struct */
|
||||||
@ -717,8 +717,8 @@ static int cfun(JanetArgs args) {
|
|||||||
} else {
|
} else {
|
||||||
t = janet_table(4);
|
t = janet_table(4);
|
||||||
janet_table_put(t, janet_csymbolv(":error"), janet_wrap_string(res.error));
|
janet_table_put(t, janet_csymbolv(":error"), janet_wrap_string(res.error));
|
||||||
janet_table_put(t, janet_csymbolv(":line"), janet_wrap_integer(res.error_mapping.line));
|
janet_table_put(t, janet_csymbolv(":start"), janet_wrap_integer(res.error_mapping.start));
|
||||||
janet_table_put(t, janet_csymbolv(":column"), janet_wrap_integer(res.error_mapping.column));
|
janet_table_put(t, janet_csymbolv(":end"), janet_wrap_integer(res.error_mapping.end));
|
||||||
if (res.macrofiber) {
|
if (res.macrofiber) {
|
||||||
janet_table_put(t, janet_csymbolv(":fiber"), janet_wrap_fiber(res.macrofiber));
|
janet_table_put(t, janet_csymbolv(":fiber"), janet_wrap_fiber(res.macrofiber));
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
i
|
i
|
||||||
(do
|
(do
|
||||||
(if (= t :string)
|
(if (= t :string)
|
||||||
(:= docstr ith)
|
(set docstr ith)
|
||||||
(array/push modifiers ith))
|
(array/push modifiers ith))
|
||||||
(if (< i len) (recur (+ i 1)))))))
|
(if (< i len) (recur (+ i 1)))))))
|
||||||
(def start (fstart 0))
|
(def start (fstart 0))
|
||||||
@ -37,7 +37,7 @@
|
|||||||
(while (< index arglen)
|
(while (< index arglen)
|
||||||
(buffer/push-string buf " ")
|
(buffer/push-string buf " ")
|
||||||
(string/pretty args.index 4 buf)
|
(string/pretty args.index 4 buf)
|
||||||
(:= index (+ index 1)))
|
(set index (+ index 1)))
|
||||||
(array/push modifiers (string buf ")\n\n" docstr))
|
(array/push modifiers (string buf ")\n\n" docstr))
|
||||||
# Build return value
|
# Build return value
|
||||||
~(def ,name ,;modifiers (fn ,name ,;(tuple/slice more start)))))
|
~(def ,name ,;modifiers (fn ,name ,;(tuple/slice more start)))))
|
||||||
@ -84,25 +84,24 @@
|
|||||||
(defn neg? "Check if x is less than 0." [x] (< x 0))
|
(defn neg? "Check if x is less than 0." [x] (< x 0))
|
||||||
(defn one? "Check if x is equal to 1." [x] (== x 1))
|
(defn one? "Check if x is equal to 1." [x] (== x 1))
|
||||||
(defn integer? "Check if x is an integer." [x] (= (type x) :integer))
|
(defn integer? "Check if x is an integer." [x] (= (type x) :integer))
|
||||||
(defn real? [x] "Check if x is a real number." (= (type x) :real))
|
(defn real? "Check if x is a real number." [x] (= (type x) :real))
|
||||||
(defn number? "Check if x is a number." [x]
|
(defn number? "Check if x is a number." [x]
|
||||||
(def t (type x))
|
(def t (type x))
|
||||||
(if (= t :integer) true (= t :real)))
|
(if (= t :integer) true (= t :real)))
|
||||||
(defn fiber? "Check if x is a fiber." [x] (= (type x) :fiber))
|
(defn fiber? "Check if x is a fiber." [x] (= (type x) :fiber))
|
||||||
(defn string? "Check if x is a string." [x] (= (type x) :string))
|
(defn string? "Check if x is a string." [x] (= (type x) :string))
|
||||||
(defn symbol? "Check if x is a symbol." [x] (= (type x) :symbol))
|
(defn symbol? "Check if x is a symbol." [x] (= (type x) :symbol))
|
||||||
(defn keyword? "Check if x is a keyword style symbol."
|
(defn keyword? "Check if x is a keyword style symbol." [x]
|
||||||
[x]
|
|
||||||
(if (not= (type x) :symbol) nil (= 58 x.0)))
|
(if (not= (type x) :symbol) nil (= 58 x.0)))
|
||||||
(defn buffer? "Check if x is a buffer." [x] (= (type x) :buffer))
|
(defn buffer? "Check if x is a buffer." [x] (= (type x) :buffer))
|
||||||
(defn function? "Check if x is a function (not a cfunction)."
|
(defn function? "Check if x is a function (not a cfunction)." [x]
|
||||||
[x] (= (type x) :function))
|
(= (type x) :function))
|
||||||
(defn cfunction? "Check if x a cfunction." [x] (= (type x) :cfunction))
|
(defn cfunction? "Check if x a cfunction." [x] (= (type x) :cfunction))
|
||||||
(defn table? [x] "Check if x a table." (= (type x) :table ))
|
(defn table? "Check if x a table." [x] (= (type x) :table ))
|
||||||
(defn struct? [x] "Check if x a struct." (= (type x) :struct))
|
(defn struct? "Check if x a struct." [x] (= (type x) :struct))
|
||||||
(defn array? [x] "Check if x is an array." (= (type x) :array))
|
(defn array? "Check if x is an array." [x] (= (type x) :array))
|
||||||
(defn tuple? [x] "Check if x is a tuple." (= (type x) :tuple))
|
(defn tuple? "Check if x is a tuple." [x] (= (type x) :tuple))
|
||||||
(defn boolean? [x] "Check if x is a boolean." (= (type x) :boolean))
|
(defn boolean? "Check if x is a boolean." [x] (= (type x) :boolean))
|
||||||
(defn bytes? "Check if x is a string, symbol, or buffer." [x]
|
(defn bytes? "Check if x is a string, symbol, or buffer." [x]
|
||||||
(def t (type x))
|
(def t (type x))
|
||||||
(if (= t :string) true (if (= t :symbol) true (= t :buffer))))
|
(if (= t :string) true (if (= t :symbol) true (= t :buffer))))
|
||||||
@ -132,9 +131,9 @@
|
|||||||
|
|
||||||
(defmacro with-idemp
|
(defmacro with-idemp
|
||||||
"Return janet code body that has been prepended
|
"Return janet code body that has been prepended
|
||||||
with a binding of form to atom. If form is a non-
|
with a binding of form to atom. If form is a non-idempotent
|
||||||
idempotent form (a function call, etc.), make sure the resulting
|
form (a function call, etc.), make sure the resulting
|
||||||
code will only call evaluate once, even if body contains multiple
|
code will only evaluate once, even if body contains multiple
|
||||||
copies of binding. In body, use binding instead of form."
|
copies of binding. In body, use binding instead of form."
|
||||||
[binding form & body]
|
[binding form & body]
|
||||||
(def $result (gensym))
|
(def $result (gensym))
|
||||||
@ -147,22 +146,16 @@
|
|||||||
,$result
|
,$result
|
||||||
(tuple 'do (tuple 'def ,binding ,$form) ,$result))))
|
(tuple 'do (tuple 'def ,binding ,$form) ,$result))))
|
||||||
|
|
||||||
# C style macros and functions for imperative sugar
|
# C style macros and functions for imperative sugar. No bitwise though.
|
||||||
(defn inc "Returns x + 1." [x] (+ x 1))
|
(defn inc "Returns x + 1." [x] (+ x 1))
|
||||||
(defn dec "Returns x - 1." [x] (- x 1))
|
(defn dec "Returns x - 1." [x] (- x 1))
|
||||||
(defmacro ++ "Increments the var x by 1." [x] ~(:= ,x (,+ ,x ,1)))
|
(defmacro ++ "Increments the var x by 1." [x] ~(set ,x (,+ ,x ,1)))
|
||||||
(defmacro -- "Decrements the var x by 1." [x] ~(:= ,x (,- ,x ,1)))
|
(defmacro -- "Decrements the var x by 1." [x] ~(set ,x (,- ,x ,1)))
|
||||||
(defmacro += "Increments the var x by n." [x n] ~(:= ,x (,+ ,x ,n)))
|
(defmacro += "Increments the var x by n." [x n] ~(set ,x (,+ ,x ,n)))
|
||||||
(defmacro -= "Decrements the vat x by n." [x n] ~(:= ,x (,- ,x ,n)))
|
(defmacro -= "Decrements the var x by n." [x n] ~(set ,x (,- ,x ,n)))
|
||||||
(defmacro *= "Shorthand for (:= x (* x n))." [x n] ~(:= ,x (,* ,x ,n)))
|
(defmacro *= "Shorthand for (set x (* x n))." [x n] ~(set ,x (,* ,x ,n)))
|
||||||
(defmacro /= "Shorthand for (:= x (/ x n))." [x n] ~(:= ,x (,/ ,x ,n)))
|
(defmacro /= "Shorthand for (set x (/ x n))." [x n] ~(set ,x (,/ ,x ,n)))
|
||||||
(defmacro %= "Shorthand for (:= x (% x n))." [x n] ~(:= ,x (,% ,x ,n)))
|
(defmacro %= "Shorthand for (set x (% x n))." [x n] ~(set ,x (,% ,x ,n)))
|
||||||
(defmacro &= "Shorthand for (:= x (& x n))." [x n] ~(:= ,x (,& ,x ,n)))
|
|
||||||
(defmacro |= "Shorthand for (:= x (| x n))." [x n] ~(:= ,x (,| ,x ,n)))
|
|
||||||
(defmacro ^= "Shorthand for (:= x (^ x n))." [x n] ~(:= ,x (,^ ,x ,n)))
|
|
||||||
(defmacro >>= "Shorthand for (:= x (>> x n))." [x n] ~(:= ,x (,>> ,x ,n)))
|
|
||||||
(defmacro <<= "Shorthand for (:= x (<< x n))." [x n] ~(:= ,x (,<< ,x ,n)))
|
|
||||||
(defmacro >>>= "Shorthand for (:= x (>>> x n))." [x n] ~(:= ,x (,>>> ,x ,n)))
|
|
||||||
|
|
||||||
(defmacro default
|
(defmacro default
|
||||||
"Define a default value for an optional argument.
|
"Define a default value for an optional argument.
|
||||||
@ -249,7 +242,7 @@
|
|||||||
(var i len)
|
(var i len)
|
||||||
(while (> i 0)
|
(while (> i 0)
|
||||||
(-- i)
|
(-- i)
|
||||||
(:= ret (if (= ret true)
|
(set ret (if (= ret true)
|
||||||
forms.i
|
forms.i
|
||||||
(tuple 'if forms.i ret))))
|
(tuple 'if forms.i ret))))
|
||||||
ret)
|
ret)
|
||||||
@ -264,7 +257,7 @@
|
|||||||
(while (> i 0)
|
(while (> i 0)
|
||||||
(-- i)
|
(-- i)
|
||||||
(def fi forms.i)
|
(def fi forms.i)
|
||||||
(:= ret (if (idempotent? fi)
|
(set ret (if (idempotent? fi)
|
||||||
(tuple 'if fi fi ret)
|
(tuple 'if fi fi ret)
|
||||||
(do
|
(do
|
||||||
(def $fi (gensym))
|
(def $fi (gensym))
|
||||||
@ -332,13 +325,13 @@
|
|||||||
(tuple 'var $iter 0)
|
(tuple 'var $iter 0)
|
||||||
(tuple 'while
|
(tuple 'while
|
||||||
(tuple/slice spreds)
|
(tuple/slice spreds)
|
||||||
(tuple := $iter (tuple + 1 $iter))
|
(tuple 'set $iter (tuple + 1 $iter))
|
||||||
sub)))
|
sub)))
|
||||||
(error (string "unexpected loop predicate: " bindings)))
|
(error (string "unexpected loop predicate: " bindings)))
|
||||||
(case verb
|
(case verb
|
||||||
:iterate (do
|
:iterate (do
|
||||||
(def $iter (gensym))
|
(def $iter (gensym))
|
||||||
(def preds @['and (tuple ':= $iter object)])
|
(def preds @['and (tuple 'set $iter object)])
|
||||||
(def subloop (doone (+ i 3) preds))
|
(def subloop (doone (+ i 3) preds))
|
||||||
(tuple 'do
|
(tuple 'do
|
||||||
(tuple 'var $iter nil)
|
(tuple 'var $iter nil)
|
||||||
@ -358,7 +351,7 @@
|
|||||||
(tuple 'while (tuple/slice preds)
|
(tuple 'while (tuple/slice preds)
|
||||||
(tuple 'def bindings $iter)
|
(tuple 'def bindings $iter)
|
||||||
subloop
|
subloop
|
||||||
(tuple ':= $iter (tuple + $iter inc)))))
|
(tuple 'set $iter (tuple + $iter inc)))))
|
||||||
:keys (do
|
:keys (do
|
||||||
(def $dict (gensym))
|
(def $dict (gensym))
|
||||||
(def $iter (gensym))
|
(def $iter (gensym))
|
||||||
@ -370,7 +363,7 @@
|
|||||||
(tuple 'while (tuple/slice preds)
|
(tuple 'while (tuple/slice preds)
|
||||||
(tuple 'def bindings $iter)
|
(tuple 'def bindings $iter)
|
||||||
subloop
|
subloop
|
||||||
(tuple ':= $iter (tuple next $dict $iter)))))
|
(tuple 'set $iter (tuple next $dict $iter)))))
|
||||||
:in (do
|
:in (do
|
||||||
(def $len (gensym))
|
(def $len (gensym))
|
||||||
(def $i (gensym))
|
(def $i (gensym))
|
||||||
@ -384,7 +377,7 @@
|
|||||||
(tuple 'while (tuple/slice preds 0)
|
(tuple 'while (tuple/slice preds 0)
|
||||||
(tuple 'def bindings (tuple get $indexed $i))
|
(tuple 'def bindings (tuple get $indexed $i))
|
||||||
subloop
|
subloop
|
||||||
(tuple ':= $i (tuple + 1 $i)))))
|
(tuple 'set $i (tuple + 1 $i)))))
|
||||||
:generate (do
|
:generate (do
|
||||||
(def $fiber (gensym))
|
(def $fiber (gensym))
|
||||||
(def $yieldval (gensym))
|
(def $yieldval (gensym))
|
||||||
@ -401,7 +394,7 @@
|
|||||||
(tuple 'while (tuple/slice preds 0)
|
(tuple 'while (tuple/slice preds 0)
|
||||||
(tuple 'def bindings $yieldval)
|
(tuple 'def bindings $yieldval)
|
||||||
subloop
|
subloop
|
||||||
(tuple := $yieldval (tuple resume $fiber)))))
|
(tuple 'set $yieldval (tuple resume $fiber)))))
|
||||||
(error (string "unexpected loop verb: " verb)))))))
|
(error (string "unexpected loop verb: " verb)))))))
|
||||||
(doone 0 nil))
|
(doone 0 nil))
|
||||||
|
|
||||||
@ -434,21 +427,23 @@
|
|||||||
(tuple fiber/new (tuple 'fn '[&] ;body)))
|
(tuple fiber/new (tuple 'fn '[&] ;body)))
|
||||||
|
|
||||||
(defn sum
|
(defn sum
|
||||||
|
"Returns the sum of xs. If xs is empty, returns 0."
|
||||||
[xs]
|
[xs]
|
||||||
(var accum 0)
|
(var accum 0)
|
||||||
(loop [x :in xs] (+= accum x))
|
(loop [x :in xs] (+= accum x))
|
||||||
accum)
|
accum)
|
||||||
|
|
||||||
(defn product
|
(defn product
|
||||||
|
"Returns the product of xs. If xs is empty, returns 1."
|
||||||
[xs]
|
[xs]
|
||||||
(var accum 1)
|
(var accum 1)
|
||||||
(loop [x :in xs] (*= accum x))
|
(loop [x :in xs] (*= accum x))
|
||||||
accum)
|
accum)
|
||||||
|
|
||||||
(defmacro if-let
|
(defmacro if-let
|
||||||
"Takes the first one or two forms in a vector and if both are true binds
|
"Make mutliple bindings, anf if all are truthy,
|
||||||
all the forms with let and evaluates the first expression else
|
evaluate the tru form. If any are false or nil, evaluate
|
||||||
evaluates the second"
|
the fal form. Bindings have the same syntax as the let macro."
|
||||||
[bindings tru fal &]
|
[bindings tru fal &]
|
||||||
(def len (length bindings))
|
(def len (length bindings))
|
||||||
(if (zero? len) (error "expected at least 1 binding"))
|
(if (zero? len) (error "expected at least 1 binding"))
|
||||||
@ -477,8 +472,7 @@
|
|||||||
(aux 0))
|
(aux 0))
|
||||||
|
|
||||||
(defmacro when-let
|
(defmacro when-let
|
||||||
"Takes the first one or two forms in vector and if true binds
|
"Same as (if-let bindings (do ;body))."
|
||||||
all the forms with let and evaluates the body"
|
|
||||||
[bindings & body]
|
[bindings & body]
|
||||||
~(if-let ,bindings (do ,;body)))
|
~(if-let ,bindings (do ,;body)))
|
||||||
|
|
||||||
@ -516,13 +510,26 @@
|
|||||||
(var ret args.0)
|
(var ret args.0)
|
||||||
(loop [i :range [0 len]]
|
(loop [i :range [0 len]]
|
||||||
(def v args.i)
|
(def v args.i)
|
||||||
(if (order v ret) (:= ret v)))
|
(if (order v ret) (set ret v)))
|
||||||
ret))
|
ret))
|
||||||
|
|
||||||
(defn max [& args] (extreme > args))
|
(defn max
|
||||||
(defn min [& args] (extreme < args))
|
"Returns the numeric maximum of the arguments."
|
||||||
(defn max-order [& args] (extreme order> args))
|
[& args] (extreme > args))
|
||||||
(defn min-order [& args] (extreme order< args))
|
|
||||||
|
(defn min
|
||||||
|
"Returns the numeric minimum of the arguments."
|
||||||
|
[& args] (extreme < args))
|
||||||
|
|
||||||
|
(defn max-order
|
||||||
|
"Returns the maximum of the arguments according to a total
|
||||||
|
order over all values."
|
||||||
|
[& args] (extreme order> args))
|
||||||
|
|
||||||
|
(defn min-order
|
||||||
|
"Returns the minimum of the arguments according to a total
|
||||||
|
order over all values."
|
||||||
|
[& args] (extreme order< args))
|
||||||
|
|
||||||
(defn first
|
(defn first
|
||||||
"Get the first element from an indexed data structure."
|
"Get the first element from an indexed data structure."
|
||||||
@ -541,7 +548,7 @@
|
|||||||
###
|
###
|
||||||
|
|
||||||
(def sort
|
(def sort
|
||||||
"Sort an array in-place. Uses quicksort and is not a stable sort."
|
"(sort xs [, by])\n\nSort an array in-place. Uses quicksort and is not a stable sort."
|
||||||
(do
|
(do
|
||||||
|
|
||||||
(defn partition
|
(defn partition
|
||||||
@ -552,11 +559,11 @@
|
|||||||
(def aj a.j)
|
(def aj a.j)
|
||||||
(when (by aj pivot)
|
(when (by aj pivot)
|
||||||
(def ai a.i)
|
(def ai a.i)
|
||||||
(:= a.i aj)
|
(set a.i aj)
|
||||||
(:= a.j ai)
|
(set a.j ai)
|
||||||
(++ i)))
|
(++ i)))
|
||||||
(:= a.hi a.i)
|
(set a.hi a.i)
|
||||||
(:= a.i pivot)
|
(set a.i pivot)
|
||||||
i)
|
i)
|
||||||
|
|
||||||
(defn sort-help
|
(defn sort-help
|
||||||
@ -567,7 +574,7 @@
|
|||||||
(sort-help a (+ piv 1) hi by))
|
(sort-help a (+ piv 1) hi by))
|
||||||
a)
|
a)
|
||||||
|
|
||||||
(fn [a by &]
|
(fn sort [a by &]
|
||||||
(sort-help a 0 (- (length a) 1) (or by order<)))))
|
(sort-help a 0 (- (length a) 1) (or by order<)))))
|
||||||
|
|
||||||
(defn sorted
|
(defn sorted
|
||||||
@ -581,7 +588,7 @@
|
|||||||
[f init ind]
|
[f init ind]
|
||||||
(var res init)
|
(var res init)
|
||||||
(loop [x :in ind]
|
(loop [x :in ind]
|
||||||
(:= res (f res x)))
|
(set res (f res x)))
|
||||||
res)
|
res)
|
||||||
|
|
||||||
(defn map
|
(defn map
|
||||||
@ -593,18 +600,18 @@
|
|||||||
(var limit (length inds.0))
|
(var limit (length inds.0))
|
||||||
(loop [i :range [0 ninds]]
|
(loop [i :range [0 ninds]]
|
||||||
(def l (length inds.i))
|
(def l (length inds.i))
|
||||||
(if (< l limit) (:= limit l)))
|
(if (< l limit) (set limit l)))
|
||||||
(def [i1 i2 i3 i4] inds)
|
(def [i1 i2 i3 i4] inds)
|
||||||
(def res (array/new limit))
|
(def res (array/new limit))
|
||||||
(case ninds
|
(case ninds
|
||||||
1 (loop [i :range [0 limit]] (:= res.i (f i1.i)))
|
1 (loop [i :range [0 limit]] (set res.i (f i1.i)))
|
||||||
2 (loop [i :range [0 limit]] (:= res.i (f i1.i i2.i)))
|
2 (loop [i :range [0 limit]] (set res.i (f i1.i i2.i)))
|
||||||
3 (loop [i :range [0 limit]] (:= res.i (f i1.i i2.i i3.i)))
|
3 (loop [i :range [0 limit]] (set res.i (f i1.i i2.i i3.i)))
|
||||||
4 (loop [i :range [0 limit]] (:= res.i (f i1.i i2.i i3.i i4.i)))
|
4 (loop [i :range [0 limit]] (set res.i (f i1.i i2.i i3.i i4.i)))
|
||||||
(loop [i :range [0 limit]]
|
(loop [i :range [0 limit]]
|
||||||
(def args (array/new ninds))
|
(def args (array/new ninds))
|
||||||
(loop [j :range [0 ninds]] (:= args.j inds.j.i))
|
(loop [j :range [0 ninds]] (set args.j inds.j.i))
|
||||||
(:= res.i (f ;args))))
|
(set res.i (f ;args))))
|
||||||
res)
|
res)
|
||||||
|
|
||||||
(defn mapcat
|
(defn mapcat
|
||||||
@ -677,7 +684,7 @@
|
|||||||
(var going true)
|
(var going true)
|
||||||
(while (if (< i len) going)
|
(while (if (< i len) going)
|
||||||
(def item ind.i)
|
(def item ind.i)
|
||||||
(if (pred item) (:= going false) (++ i)))
|
(if (pred item) (set going false) (++ i)))
|
||||||
(if going nil i))
|
(if going nil i))
|
||||||
|
|
||||||
(defn find
|
(defn find
|
||||||
@ -770,7 +777,7 @@
|
|||||||
[ind]
|
[ind]
|
||||||
(var res true)
|
(var res true)
|
||||||
(loop [x :in ind :while res]
|
(loop [x :in ind :while res]
|
||||||
(if x nil (:= res x)))
|
(if x nil (set res x)))
|
||||||
res)
|
res)
|
||||||
|
|
||||||
(defn reverse
|
(defn reverse
|
||||||
@ -795,8 +802,8 @@ value, one key will be ignored."
|
|||||||
ret)
|
ret)
|
||||||
|
|
||||||
(defn zipcoll
|
(defn zipcoll
|
||||||
"Creates an table or tuple from two arrays/tuples. If a third argument of
|
"Creates an table or tuple from two arrays/tuples.
|
||||||
:struct is given result is struct else is table. Returns a new table."
|
Returns a new table."
|
||||||
[keys vals]
|
[keys vals]
|
||||||
(def res @{})
|
(def res @{})
|
||||||
(def lk (length keys))
|
(def lk (length keys))
|
||||||
@ -811,7 +818,7 @@ value, one key will be ignored."
|
|||||||
The key then, is associated to the function's return value"
|
The key then, is associated to the function's return value"
|
||||||
[coll a-key a-function & args]
|
[coll a-key a-function & args]
|
||||||
(def old-value coll.a-key)
|
(def old-value coll.a-key)
|
||||||
(:= coll.a-key (a-function old-value ;args)))
|
(set coll.a-key (a-function old-value ;args)))
|
||||||
|
|
||||||
(defn merge-into
|
(defn merge-into
|
||||||
"Merges multiple tables/structs into a table. If a key appears in more than one
|
"Merges multiple tables/structs into a table. If a key appears in more than one
|
||||||
@ -820,7 +827,7 @@ value, one key will be ignored."
|
|||||||
[tab & colls]
|
[tab & colls]
|
||||||
(loop [c :in colls
|
(loop [c :in colls
|
||||||
key :keys c]
|
key :keys c]
|
||||||
(:= tab.key c.key))
|
(set tab.key c.key))
|
||||||
tab)
|
tab)
|
||||||
|
|
||||||
(defn merge
|
(defn merge
|
||||||
@ -831,7 +838,7 @@ value, one key will be ignored."
|
|||||||
(def container @{})
|
(def container @{})
|
||||||
(loop [c :in colls
|
(loop [c :in colls
|
||||||
key :keys c]
|
key :keys c]
|
||||||
(:= container.key c.key))
|
(set container.key c.key))
|
||||||
container)
|
container)
|
||||||
|
|
||||||
(defn keys
|
(defn keys
|
||||||
@ -841,7 +848,7 @@ value, one key will be ignored."
|
|||||||
(var k (next x nil))
|
(var k (next x nil))
|
||||||
(while (not= nil k)
|
(while (not= nil k)
|
||||||
(array/push arr k)
|
(array/push arr k)
|
||||||
(:= k (next x k)))
|
(set k (next x k)))
|
||||||
arr)
|
arr)
|
||||||
|
|
||||||
(defn values
|
(defn values
|
||||||
@ -851,7 +858,7 @@ value, one key will be ignored."
|
|||||||
(var k (next x nil))
|
(var k (next x nil))
|
||||||
(while (not= nil k)
|
(while (not= nil k)
|
||||||
(array/push arr x.k)
|
(array/push arr x.k)
|
||||||
(:= k (next x k)))
|
(set k (next x k)))
|
||||||
arr)
|
arr)
|
||||||
|
|
||||||
(defn pairs
|
(defn pairs
|
||||||
@ -861,7 +868,7 @@ value, one key will be ignored."
|
|||||||
(var k (next x nil))
|
(var k (next x nil))
|
||||||
(while (not= nil k)
|
(while (not= nil k)
|
||||||
(array/push arr (tuple k x.k))
|
(array/push arr (tuple k x.k))
|
||||||
(:= k (next x k)))
|
(set k (next x k)))
|
||||||
arr)
|
arr)
|
||||||
|
|
||||||
(defn frequencies
|
(defn frequencies
|
||||||
@ -871,7 +878,7 @@ value, one key will be ignored."
|
|||||||
(loop
|
(loop
|
||||||
[x :in ind]
|
[x :in ind]
|
||||||
(def n freqs.x)
|
(def n freqs.x)
|
||||||
(:= freqs.x (if n (+ 1 n) 1)))
|
(set freqs.x (if n (+ 1 n) 1)))
|
||||||
freqs)
|
freqs)
|
||||||
|
|
||||||
(defn interleave
|
(defn interleave
|
||||||
@ -892,7 +899,7 @@ value, one key will be ignored."
|
|||||||
[xs]
|
[xs]
|
||||||
(def ret @[])
|
(def ret @[])
|
||||||
(def seen @{})
|
(def seen @{})
|
||||||
(loop [x :in xs] (if seen.x nil (do (:= seen.x true) (array/push ret x))))
|
(loop [x :in xs] (if seen.x nil (do (set seen.x true) (array/push ret x))))
|
||||||
ret)
|
ret)
|
||||||
|
|
||||||
(defn flatten-into
|
(defn flatten-into
|
||||||
@ -925,7 +932,7 @@ value, one key will be ignored."
|
|||||||
[sep ind]
|
[sep ind]
|
||||||
(def len (length ind))
|
(def len (length ind))
|
||||||
(def ret (array/new (- (* 2 len) 1)))
|
(def ret (array/new (- (* 2 len) 1)))
|
||||||
(if (> len 0) (:= ret.0 ind.0))
|
(if (> len 0) (set ret.0 ind.0))
|
||||||
(var i 1)
|
(var i 1)
|
||||||
(while (< i len)
|
(while (< i len)
|
||||||
(array/push ret sep ind.i)
|
(array/push ret sep ind.i)
|
||||||
@ -977,7 +984,7 @@ value, one key will be ignored."
|
|||||||
$dict expr
|
$dict expr
|
||||||
~(if (dictionary? ,$dict)
|
~(if (dictionary? ,$dict)
|
||||||
,((fn aux []
|
,((fn aux []
|
||||||
(:= key (next pattern key))
|
(set key (next pattern key))
|
||||||
(if (= key nil)
|
(if (= key nil)
|
||||||
(onmatch)
|
(onmatch)
|
||||||
(match-1 (get pattern key) (tuple get $dict key) aux seen))))
|
(match-1 (get pattern key) (tuple get $dict key) aux seen))))
|
||||||
@ -1035,7 +1042,7 @@ value, one key will be ignored."
|
|||||||
(def oldcur current)
|
(def oldcur current)
|
||||||
(def spacer
|
(def spacer
|
||||||
(if (<= maxcol (+ current (length word) 1))
|
(if (<= maxcol (+ current (length word) 1))
|
||||||
(do (:= current 0) "\n ")
|
(do (set current 0) "\n ")
|
||||||
(do (++ current) " ")))
|
(do (++ current) " ")))
|
||||||
(+= current (length word))
|
(+= current (length word))
|
||||||
(if (> oldcur 0)
|
(if (> oldcur 0)
|
||||||
@ -1052,7 +1059,7 @@ value, one key will be ignored."
|
|||||||
(if (> (length word) 0) (pushword))
|
(if (> (length word) 0) (pushword))
|
||||||
(when (= b 10)
|
(when (= b 10)
|
||||||
(buffer/push-string buf "\n ")
|
(buffer/push-string buf "\n ")
|
||||||
(:= current 0)))))
|
(set current 0)))))
|
||||||
|
|
||||||
# Last word
|
# Last word
|
||||||
(pushword)
|
(pushword)
|
||||||
@ -1066,8 +1073,18 @@ value, one key will be ignored."
|
|||||||
(if (not x)
|
(if (not x)
|
||||||
(print "symbol " sym " not found.")
|
(print "symbol " sym " not found.")
|
||||||
(do
|
(do
|
||||||
|
(def bind-type
|
||||||
|
(string " "
|
||||||
|
(cond
|
||||||
|
x:ref (string :var " (" (type (get x:ref 0)) ")")
|
||||||
|
x:macro :macro
|
||||||
|
(type x:value))
|
||||||
|
"\n"))
|
||||||
(def d x:doc)
|
(def d x:doc)
|
||||||
(print "\n\n" (if d (doc-format d) "no documentation found.") "\n\n"))))
|
(print "\n\n"
|
||||||
|
(if d bind-type "")
|
||||||
|
(if d (doc-format d) "no documentation found.")
|
||||||
|
"\n\n"))))
|
||||||
|
|
||||||
(defmacro doc
|
(defmacro doc
|
||||||
"Shows documentation for the given symbol."
|
"Shows documentation for the given symbol."
|
||||||
@ -1089,7 +1106,7 @@ value, one key will be ignored."
|
|||||||
(var key (next t nil))
|
(var key (next t nil))
|
||||||
(while (not= nil key)
|
(while (not= nil key)
|
||||||
(put newt (macex1 key) (on-value t.key))
|
(put newt (macex1 key) (on-value t.key))
|
||||||
(:= key (next t key)))
|
(set key (next t key)))
|
||||||
newt)
|
newt)
|
||||||
|
|
||||||
(defn expand-bindings [x]
|
(defn expand-bindings [x]
|
||||||
@ -1137,7 +1154,7 @@ value, one key will be ignored."
|
|||||||
(tuple t.0 (qq t.1)))
|
(tuple t.0 (qq t.1)))
|
||||||
|
|
||||||
(def specs
|
(def specs
|
||||||
{':= expanddef
|
{'set expanddef
|
||||||
'def expanddef
|
'def expanddef
|
||||||
'do expandall
|
'do expandall
|
||||||
'fn expandfn
|
'fn expandfn
|
||||||
@ -1167,19 +1184,24 @@ value, one key will be ignored."
|
|||||||
x))
|
x))
|
||||||
ret)
|
ret)
|
||||||
|
|
||||||
(defn all [pred xs]
|
(defn all
|
||||||
|
"Returns true if all xs are truthy, otherwise the first false or nil value."
|
||||||
|
[pred xs]
|
||||||
(var ret true)
|
(var ret true)
|
||||||
(loop [x :in xs :while ret] (:= ret (pred x)))
|
(loop [x :in xs :while ret] (set ret (pred x)))
|
||||||
ret)
|
ret)
|
||||||
|
|
||||||
(defn some [pred xs]
|
(defn some
|
||||||
|
"Returns false if all xs are false or nil, otherwise returns the first true value."
|
||||||
|
[pred xs]
|
||||||
(var ret nil)
|
(var ret nil)
|
||||||
(loop [x :in xs :while (not ret)] (if-let [y (pred x)] (:= ret y)))
|
(loop [x :in xs :while (not ret)] (if-let [y (pred x)] (set ret y)))
|
||||||
ret)
|
ret)
|
||||||
|
|
||||||
(defn deep-not= [x y]
|
(defn deep-not=
|
||||||
"Like not=, but mutable types (arrays, tables, buffers) are considered
|
"Like not=, but mutable types (arrays, tables, buffers) are considered
|
||||||
equal if they have identical structure. Much slower than not=."
|
equal if they have identical structure. Much slower than not=."
|
||||||
|
[x y]
|
||||||
(def tx (type x))
|
(def tx (type x))
|
||||||
(or
|
(or
|
||||||
(not= tx (type y))
|
(not= tx (type y))
|
||||||
@ -1191,9 +1213,10 @@ value, one key will be ignored."
|
|||||||
:buffer (not= (string x) (string y))
|
:buffer (not= (string x) (string y))
|
||||||
(not= x y))))
|
(not= x y))))
|
||||||
|
|
||||||
(defn deep= [x y]
|
(defn deep=
|
||||||
"Like =, but mutable types (arrays, tables, buffers) are considered
|
"Like =, but mutable types (arrays, tables, buffers) are considered
|
||||||
equal if they have identical structure. Much slower than =."
|
equal if they have identical structure. Much slower than =."
|
||||||
|
[x y]
|
||||||
(not (deep-not= x y)))
|
(not (deep-not= x y)))
|
||||||
|
|
||||||
(defn macex
|
(defn macex
|
||||||
@ -1205,8 +1228,8 @@ value, one key will be ignored."
|
|||||||
(while (deep-not= current previous)
|
(while (deep-not= current previous)
|
||||||
(if (> (++ counter) 200)
|
(if (> (++ counter) 200)
|
||||||
(error "macro expansion too nested"))
|
(error "macro expansion too nested"))
|
||||||
(:= previous current)
|
(set previous current)
|
||||||
(:= current (macex1 current)))
|
(set current (macex1 current)))
|
||||||
current)
|
current)
|
||||||
|
|
||||||
###
|
###
|
||||||
@ -1216,6 +1239,9 @@ value, one key will be ignored."
|
|||||||
###
|
###
|
||||||
|
|
||||||
(defn make-env
|
(defn make-env
|
||||||
|
"Create a new environment table. The new environment
|
||||||
|
will inherit bindings from the parent environment, but new
|
||||||
|
bindings will not pollute the parent environment."
|
||||||
[parent &]
|
[parent &]
|
||||||
(def parent (if parent parent _env))
|
(def parent (if parent parent _env))
|
||||||
(def newenv (table/setproto @{} parent))
|
(def newenv (table/setproto @{} parent))
|
||||||
@ -1251,12 +1277,12 @@ value, one key will be ignored."
|
|||||||
(if (= (type res) :function)
|
(if (= (type res) :function)
|
||||||
(res)
|
(res)
|
||||||
(do
|
(do
|
||||||
(:= good false)
|
(set good false)
|
||||||
(def {:error err :line errl :column errc :fiber errf} res)
|
(def {:error err :start start :end end :fiber errf} res)
|
||||||
(onstatus
|
(onstatus
|
||||||
:compile
|
:compile
|
||||||
(if (< 0 errl)
|
(if (<= 0 start)
|
||||||
(string err "\n in a form at line " errl ", column " errc)
|
(string err "\n at (" start ":" end ")")
|
||||||
err)
|
err)
|
||||||
errf
|
errf
|
||||||
where))))
|
where))))
|
||||||
@ -1266,7 +1292,7 @@ value, one key will be ignored."
|
|||||||
(if going (onstatus (fiber/status f) res f where))))
|
(if going (onstatus (fiber/status f) res f where))))
|
||||||
|
|
||||||
(def oldenv *env*)
|
(def oldenv *env*)
|
||||||
(:= *env* env)
|
(set *env* env)
|
||||||
|
|
||||||
# Run loop
|
# Run loop
|
||||||
(def buf @"")
|
(def buf @"")
|
||||||
@ -1274,22 +1300,21 @@ value, one key will be ignored."
|
|||||||
(buffer/clear buf)
|
(buffer/clear buf)
|
||||||
(chunks buf p)
|
(chunks buf p)
|
||||||
(var pindex 0)
|
(var pindex 0)
|
||||||
|
(var pstatus nil)
|
||||||
(def len (length buf))
|
(def len (length buf))
|
||||||
(if (= len 0) (:= going false))
|
(if (= len 0) (set going false))
|
||||||
(while (> len pindex)
|
(while (> len pindex)
|
||||||
(+= pindex (parser/consume p buf pindex))
|
(+= pindex (parser/consume p buf pindex))
|
||||||
(case (parser/status p)
|
(while (= (set pstatus (parser/status p)) :full)
|
||||||
:full (eval1 (parser/produce p))
|
(eval1 (parser/produce p)))
|
||||||
:error (do
|
(when (= pstatus :error)
|
||||||
(def (line col) (parser/where p))
|
(onstatus :parse
|
||||||
(onstatus :parse
|
(string (parser/error p)
|
||||||
(string (parser/error p)
|
" around byte " (parser/where p))
|
||||||
" on line " line
|
nil
|
||||||
", column " col)
|
where))))
|
||||||
nil
|
|
||||||
where)))))
|
|
||||||
|
|
||||||
(:= *env* oldenv)
|
(set *env* oldenv)
|
||||||
|
|
||||||
env)
|
env)
|
||||||
|
|
||||||
@ -1309,7 +1334,7 @@ value, one key will be ignored."
|
|||||||
"\n")
|
"\n")
|
||||||
(when f
|
(when f
|
||||||
(loop
|
(loop
|
||||||
[nf :in (reverse (fiber/lineage f))
|
[nf :in (reverse (debug/lineage f))
|
||||||
:before (file/write stderr " (fiber)\n")
|
:before (file/write stderr " (fiber)\n")
|
||||||
{:function func
|
{:function func
|
||||||
:tail tail
|
:tail tail
|
||||||
@ -1317,8 +1342,8 @@ value, one key will be ignored."
|
|||||||
:c c
|
:c c
|
||||||
:name name
|
:name name
|
||||||
:source source
|
:source source
|
||||||
:line source-line
|
:source-start start
|
||||||
:column source-col} :in (fiber/stack nf)]
|
:source-end end} :in (debug/stack nf)]
|
||||||
(file/write stderr " in")
|
(file/write stderr " in")
|
||||||
(when c (file/write stderr " cfunction"))
|
(when c (file/write stderr " cfunction"))
|
||||||
(if name
|
(if name
|
||||||
@ -1327,14 +1352,15 @@ value, one key will be ignored."
|
|||||||
(if source
|
(if source
|
||||||
(do
|
(do
|
||||||
(file/write stderr " [" source "]")
|
(file/write stderr " [" source "]")
|
||||||
(if source-line
|
(if start
|
||||||
(file/write
|
(file/write
|
||||||
stderr
|
stderr
|
||||||
" on line "
|
" at ("
|
||||||
(string source-line)
|
(string start)
|
||||||
", column "
|
":"
|
||||||
(string source-col)))))
|
(string end)
|
||||||
(if (and (not source-line) pc)
|
")"))))
|
||||||
|
(if (and (not start) pc)
|
||||||
(file/write stderr " (pc=" (string pc) ")"))
|
(file/write stderr " (pc=" (string pc) ")"))
|
||||||
(when tail (file/write stderr " (tailcall)"))
|
(when tail (file/write stderr " (tailcall)"))
|
||||||
(file/write stderr "\n"))))
|
(file/write stderr "\n"))))
|
||||||
@ -1346,7 +1372,7 @@ value, one key will be ignored."
|
|||||||
(var state (string str))
|
(var state (string str))
|
||||||
(defn chunks [buf _]
|
(defn chunks [buf _]
|
||||||
(def ret state)
|
(def ret state)
|
||||||
(:= state nil)
|
(set state nil)
|
||||||
(when ret
|
(when ret
|
||||||
(buffer/push-string buf str)
|
(buffer/push-string buf str)
|
||||||
(buffer/push-string buf "\n")))
|
(buffer/push-string buf "\n")))
|
||||||
@ -1354,7 +1380,7 @@ value, one key will be ignored."
|
|||||||
(run-context *env* chunks
|
(run-context *env* chunks
|
||||||
(fn [sig x f source]
|
(fn [sig x f source]
|
||||||
(if (= sig :dead)
|
(if (= sig :dead)
|
||||||
(:= returnval x)
|
(set returnval x)
|
||||||
(status-pp sig x f source)))
|
(status-pp sig x f source)))
|
||||||
"eval")
|
"eval")
|
||||||
returnval)
|
returnval)
|
||||||
@ -1410,7 +1436,8 @@ value, one key will be ignored."
|
|||||||
path))
|
path))
|
||||||
|
|
||||||
(def require
|
(def require
|
||||||
"Require a module with the given name. Will search all of the paths in
|
"(require module)\n\n
|
||||||
|
Require a module with the given name. Will search all of the paths in
|
||||||
module/paths, then the path as a raw file path. Returns the new environment
|
module/paths, then the path as a raw file path. Returns the new environment
|
||||||
returned from compiling and running the file."
|
returned from compiling and running the file."
|
||||||
(do
|
(do
|
||||||
@ -1446,8 +1473,8 @@ value, one key will be ignored."
|
|||||||
check
|
check
|
||||||
(do
|
(do
|
||||||
(def newenv (make-env))
|
(def newenv (make-env))
|
||||||
(:= cache.path newenv)
|
(set cache.path newenv)
|
||||||
(:= loading.path true)
|
(set loading.path true)
|
||||||
(def f (find-mod path))
|
(def f (find-mod path))
|
||||||
(if f
|
(if f
|
||||||
(do
|
(do
|
||||||
@ -1466,10 +1493,13 @@ value, one key will be ignored."
|
|||||||
(if (not n)
|
(if (not n)
|
||||||
(error (string "could not open file for module " path)))
|
(error (string "could not open file for module " path)))
|
||||||
((native n) newenv)))
|
((native n) newenv)))
|
||||||
(:= loading.path false)
|
(set loading.path false)
|
||||||
newenv)))))
|
newenv)))))
|
||||||
|
|
||||||
(defn import*
|
(defn import*
|
||||||
|
"Import a module into a given environment table. This is the
|
||||||
|
functional form of (import ...) that expects and explicit environment
|
||||||
|
table."
|
||||||
[env path & args]
|
[env path & args]
|
||||||
(def targs (table ;args))
|
(def targs (table ;args))
|
||||||
(def {:as as
|
(def {:as as
|
||||||
@ -1483,7 +1513,7 @@ value, one key will be ignored."
|
|||||||
(when (not v:private)
|
(when (not v:private)
|
||||||
(def newv (table/setproto @{:private true} v))
|
(def newv (table/setproto @{:private true} v))
|
||||||
(put env (symbol prefix k) newv))
|
(put env (symbol prefix k) newv))
|
||||||
(:= k (next newenv k))))
|
(set k (next newenv k))))
|
||||||
|
|
||||||
(defmacro import
|
(defmacro import
|
||||||
"Import a module. First requires the module, and then merges its
|
"Import a module. First requires the module, and then merges its
|
||||||
@ -1507,6 +1537,7 @@ value, one key will be ignored."
|
|||||||
(def newenv (make-env))
|
(def newenv (make-env))
|
||||||
(default chunks (fn [buf _] (file/read stdin :line buf)))
|
(default chunks (fn [buf _] (file/read stdin :line buf)))
|
||||||
(default onsignal (fn [sig x f source]
|
(default onsignal (fn [sig x f source]
|
||||||
|
(put newenv '_fiber @{:value f})
|
||||||
(case sig
|
(case sig
|
||||||
:dead (do
|
:dead (do
|
||||||
(put newenv '_ @{:value x})
|
(put newenv '_ @{:value x})
|
||||||
@ -1519,9 +1550,9 @@ value, one key will be ignored."
|
|||||||
[env &]
|
[env &]
|
||||||
(default env *env*)
|
(default env *env*)
|
||||||
(def envs @[])
|
(def envs @[])
|
||||||
(do (var e env) (while e (array/push envs e) (:= e (table/getproto e))))
|
(do (var e env) (while e (array/push envs e) (set e (table/getproto e))))
|
||||||
(def symbol-set @{})
|
(def symbol-set @{})
|
||||||
(loop [envi :in envs
|
(loop [envi :in envs
|
||||||
k :keys envi]
|
k :keys envi]
|
||||||
(:= symbol-set.k true))
|
(set symbol-set.k true))
|
||||||
(sort (keys symbol-set)))
|
(sort (keys symbol-set)))
|
||||||
|
@ -324,7 +324,7 @@ static const JanetReg cfuns[] = {
|
|||||||
"(buffer & xs)\n\n"
|
"(buffer & xs)\n\n"
|
||||||
"Creates a new buffer by concatenating values together. Values are "
|
"Creates a new buffer by concatenating values together. Values are "
|
||||||
"converted to bytes via describe if they are not byte sequences. Returns "
|
"converted to bytes via describe if they are not byte sequences. Returns "
|
||||||
"the new symbol."
|
"the new buffer."
|
||||||
},
|
},
|
||||||
{"abstract?", janet_core_is_abstract,
|
{"abstract?", janet_core_is_abstract,
|
||||||
"(abstract? x)\n\n"
|
"(abstract? x)\n\n"
|
||||||
@ -384,7 +384,7 @@ static const JanetReg cfuns[] = {
|
|||||||
{"gcsetinterval", janet_core_gcsetinterval,
|
{"gcsetinterval", janet_core_gcsetinterval,
|
||||||
"(gcsetinterval interval)\n\n"
|
"(gcsetinterval interval)\n\n"
|
||||||
"Set an integer number of bytes to allocate before running garbage collection. "
|
"Set an integer number of bytes to allocate before running garbage collection. "
|
||||||
"Low values interval will be slower but use less memory. "
|
"Low valuesi for interval will be slower but use less memory. "
|
||||||
"High values will be faster but use more memory."
|
"High values will be faster but use more memory."
|
||||||
},
|
},
|
||||||
{"gcinterval", janet_core_gcinterval,
|
{"gcinterval", janet_core_gcinterval,
|
||||||
@ -406,9 +406,9 @@ static const JanetReg cfuns[] = {
|
|||||||
"\t:string\n"
|
"\t:string\n"
|
||||||
"\t:buffer\n"
|
"\t:buffer\n"
|
||||||
"\t:symbol\n"
|
"\t:symbol\n"
|
||||||
"\t:abstract\n"
|
|
||||||
"\t:function\n"
|
"\t:function\n"
|
||||||
"\t:cfunction"
|
"\t:cfunction\n\n"
|
||||||
|
"or another symbol for an abstract type."
|
||||||
},
|
},
|
||||||
{"next", janet_core_next,
|
{"next", janet_core_next,
|
||||||
"(next dict key)\n\n"
|
"(next dict key)\n\n"
|
||||||
@ -711,25 +711,25 @@ JanetTable *janet_core_env(void) {
|
|||||||
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||||
"values. Division by two integers uses truncating division.");
|
"values. Division by two integers uses truncating division.");
|
||||||
templatize_varop(env, JANET_FUN_BAND, "&", -1, -1, JOP_BAND,
|
templatize_varop(env, JANET_FUN_BAND, "band", -1, -1, JOP_BAND,
|
||||||
"(& & xs)\n\n"
|
"(band & xs)\n\n"
|
||||||
"Returns the bitwise and of all values in xs. Each x in xs must be an integer.");
|
"Returns the bitwise and of all values in xs. Each x in xs must be an integer.");
|
||||||
templatize_varop(env, JANET_FUN_BOR, "|", 0, 0, JOP_BOR,
|
templatize_varop(env, JANET_FUN_BOR, "bor", 0, 0, JOP_BOR,
|
||||||
"(| & xs)\n\n"
|
"(bor & xs)\n\n"
|
||||||
"Returns the bitwise or of all values in xs. Each x in xs must be an integer.");
|
"Returns the bitwise or of all values in xs. Each x in xs must be an integer.");
|
||||||
templatize_varop(env, JANET_FUN_BXOR, "^", 0, 0, JOP_BXOR,
|
templatize_varop(env, JANET_FUN_BXOR, "bxor", 0, 0, JOP_BXOR,
|
||||||
"(^ & xs)\n\n"
|
"(bxor & xs)\n\n"
|
||||||
"Returns the bitwise xor of all values in xs. Each in xs must be an integer.");
|
"Returns the bitwise xor of all values in xs. Each in xs must be an integer.");
|
||||||
templatize_varop(env, JANET_FUN_LSHIFT, "<<", 1, 1, JOP_SHIFT_LEFT,
|
templatize_varop(env, JANET_FUN_LSHIFT, "blshift", 1, 1, JOP_SHIFT_LEFT,
|
||||||
"(<< x & shifts)\n\n"
|
"(blshift x & shifts)\n\n"
|
||||||
"Returns the value of x bit shifted left by the sum of all values in shifts. x "
|
"Returns the value of x bit shifted left by the sum of all values in shifts. x "
|
||||||
"and each element in shift must be an integer.");
|
"and each element in shift must be an integer.");
|
||||||
templatize_varop(env, JANET_FUN_RSHIFT, ">>", 1, 1, JOP_SHIFT_RIGHT,
|
templatize_varop(env, JANET_FUN_RSHIFT, "brshift", 1, 1, JOP_SHIFT_RIGHT,
|
||||||
"(>> x & shifts)\n\n"
|
"(brshift x & shifts)\n\n"
|
||||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||||
"and each element in shift must be an integer.");
|
"and each element in shift must be an integer.");
|
||||||
templatize_varop(env, JANET_FUN_RSHIFTU, ">>>", 1, 1, JOP_SHIFT_RIGHT_UNSIGNED,
|
templatize_varop(env, JANET_FUN_RSHIFTU, "brushift", 1, 1, JOP_SHIFT_RIGHT_UNSIGNED,
|
||||||
"(>> x & shifts)\n\n"
|
"(brushift x & shifts)\n\n"
|
||||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||||
"and each element in shift must be an integer. The sign of x is not preserved, so "
|
"and each element in shift must be an integer. The sign of x is not preserved, so "
|
||||||
"for positive shifts the return value will always be positive.");
|
"for positive shifts the return value will always be positive.");
|
||||||
@ -801,6 +801,7 @@ JanetTable *janet_core_env(void) {
|
|||||||
janet_lib_os(args);
|
janet_lib_os(args);
|
||||||
janet_lib_parse(args);
|
janet_lib_parse(args);
|
||||||
janet_lib_compile(args);
|
janet_lib_compile(args);
|
||||||
|
janet_lib_debug(args);
|
||||||
janet_lib_string(args);
|
janet_lib_string(args);
|
||||||
janet_lib_marsh(args);
|
janet_lib_marsh(args);
|
||||||
#ifdef JANET_ASSEMBLER
|
#ifdef JANET_ASSEMBLER
|
||||||
|
313
src/core/debug.c
Normal file
313
src/core/debug.c
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Calvin Rose
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <janet/janet.h>
|
||||||
|
#include "gc.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
/* Implements functionality to build a debugger from within janet.
|
||||||
|
* The repl should also be able to serve as pretty featured debugger
|
||||||
|
* out of the box. */
|
||||||
|
|
||||||
|
/* Add a break point to a function */
|
||||||
|
int janet_debug_break(JanetFuncDef *def, int32_t pc) {
|
||||||
|
if (pc >= def->bytecode_length || pc < 0)
|
||||||
|
return 1;
|
||||||
|
def->bytecode[pc] |= 0x80;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a break point from a function */
|
||||||
|
int janet_debug_unbreak(JanetFuncDef *def, int32_t pc) {
|
||||||
|
if (pc >= def->bytecode_length || pc < 0)
|
||||||
|
return 1;
|
||||||
|
def->bytecode[pc] &= ~((uint32_t)0x80);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find a location for a breakpoint given a source file an
|
||||||
|
* location.
|
||||||
|
*/
|
||||||
|
int janet_debug_find(
|
||||||
|
JanetFuncDef **def_out, int32_t *pc_out,
|
||||||
|
const uint8_t *source, int32_t offset) {
|
||||||
|
/* Scan the heap for right func def */
|
||||||
|
JanetGCMemoryHeader *current = janet_vm_blocks;
|
||||||
|
/* Keep track of the best source mapping we have seen so far */
|
||||||
|
int32_t besti = -1;
|
||||||
|
int32_t best_range = INT32_MAX;
|
||||||
|
JanetFuncDef *best_def = NULL;
|
||||||
|
while (NULL != current) {
|
||||||
|
if ((current->flags & JANET_MEM_TYPEBITS) == JANET_MEMORY_FUNCDEF) {
|
||||||
|
JanetFuncDef *def = (JanetFuncDef *)(current + 1);
|
||||||
|
if (def->sourcemap &&
|
||||||
|
def->source &&
|
||||||
|
!janet_string_compare(source, def->source)) {
|
||||||
|
/* Correct source file, check mappings. The chosen
|
||||||
|
* pc index is the first match with the smallest range. */
|
||||||
|
int32_t i;
|
||||||
|
for (i = 0; i < def->bytecode_length; i++) {
|
||||||
|
int32_t start = def->sourcemap[i].start;
|
||||||
|
int32_t end = def->sourcemap[i].end;
|
||||||
|
if (end - start < best_range &&
|
||||||
|
start <= offset &&
|
||||||
|
end >= offset) {
|
||||||
|
best_range = end - start;
|
||||||
|
besti = i;
|
||||||
|
best_def = def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
if (best_def) {
|
||||||
|
*def_out = best_def;
|
||||||
|
*pc_out = besti;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CFuns
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Helper to find funcdef and bytecode offset to insert or remove breakpoints.
|
||||||
|
* Takes a source file name and byte offset. */
|
||||||
|
static int helper_find(JanetArgs args, JanetFuncDef **def, int32_t *bytecode_offset) {
|
||||||
|
const uint8_t *source;
|
||||||
|
int32_t source_offset;
|
||||||
|
JANET_FIXARITY(args, 2);
|
||||||
|
JANET_ARG_STRING(source, args, 0);
|
||||||
|
JANET_ARG_INTEGER(source_offset, args, 1);
|
||||||
|
if (janet_debug_find(
|
||||||
|
def, bytecode_offset, source, source_offset)) {
|
||||||
|
JANET_THROW(args, "could not find breakpoint");
|
||||||
|
}
|
||||||
|
JANET_RETURN_NIL(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper to find funcdef and bytecode offset to insert or remove breakpoints.
|
||||||
|
* Takes a function and byte offset*/
|
||||||
|
static int helper_find_fun(JanetArgs args, JanetFuncDef **def, int32_t *bytecode_offset) {
|
||||||
|
JanetFunction *func;
|
||||||
|
int32_t offset = 0;
|
||||||
|
JANET_MINARITY(args, 1);
|
||||||
|
JANET_MAXARITY(args, 2);
|
||||||
|
JANET_ARG_FUNCTION(func, args, 0);
|
||||||
|
if (args.n == 2) {
|
||||||
|
JANET_ARG_INTEGER(offset, args, 1);
|
||||||
|
}
|
||||||
|
*def = func->def;
|
||||||
|
*bytecode_offset = offset;
|
||||||
|
JANET_RETURN_NIL(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cfun_break(JanetArgs args) {
|
||||||
|
JanetFuncDef *def;
|
||||||
|
int32_t offset;
|
||||||
|
int status = helper_find(args, &def, &offset);
|
||||||
|
if (status == 0) janet_debug_break(def, offset);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cfun_unbreak(JanetArgs args) {
|
||||||
|
JanetFuncDef *def;
|
||||||
|
int32_t offset;
|
||||||
|
int status = helper_find(args, &def, &offset);
|
||||||
|
if (status == 0) janet_debug_unbreak(def, offset);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cfun_fbreak(JanetArgs args) {
|
||||||
|
JanetFuncDef *def;
|
||||||
|
int32_t offset;
|
||||||
|
int status = helper_find_fun(args, &def, &offset);
|
||||||
|
if (status == 0) {
|
||||||
|
if (janet_debug_break(def, offset)) {
|
||||||
|
JANET_THROW(args, "could not find breakpoint");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cfun_unfbreak(JanetArgs args) {
|
||||||
|
JanetFuncDef *def;
|
||||||
|
int32_t offset;
|
||||||
|
int status = helper_find_fun(args, &def, &offset);
|
||||||
|
if (status == 0) {
|
||||||
|
if (janet_debug_unbreak(def, offset)) {
|
||||||
|
JANET_THROW(args, "could not find breakpoint");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cfun_lineage(JanetArgs args) {
|
||||||
|
JanetFiber *fiber;
|
||||||
|
JanetArray *array;
|
||||||
|
JANET_FIXARITY(args, 1);
|
||||||
|
JANET_ARG_FIBER(fiber, args, 0);
|
||||||
|
array = janet_array(0);
|
||||||
|
while (fiber) {
|
||||||
|
janet_array_push(array, janet_wrap_fiber(fiber));
|
||||||
|
fiber = fiber->child;
|
||||||
|
}
|
||||||
|
JANET_RETURN_ARRAY(args, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract info from one stack frame */
|
||||||
|
static Janet doframe(JanetStackFrame *frame) {
|
||||||
|
int32_t off;
|
||||||
|
JanetTable *t = janet_table(3);
|
||||||
|
JanetFuncDef *def = NULL;
|
||||||
|
if (frame->func) {
|
||||||
|
janet_table_put(t, janet_csymbolv(":function"), janet_wrap_function(frame->func));
|
||||||
|
def = frame->func->def;
|
||||||
|
if (def->name) {
|
||||||
|
janet_table_put(t, janet_csymbolv(":name"), janet_wrap_string(def->name));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
JanetCFunction cfun = (JanetCFunction)(frame->pc);
|
||||||
|
if (cfun) {
|
||||||
|
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
|
||||||
|
if (!janet_checktype(name, JANET_NIL)) {
|
||||||
|
janet_table_put(t, janet_csymbolv(":name"), name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
janet_table_put(t, janet_csymbolv(":c"), janet_wrap_true());
|
||||||
|
}
|
||||||
|
if (frame->flags & JANET_STACKFRAME_TAILCALL) {
|
||||||
|
janet_table_put(t, janet_csymbolv(":tail"), janet_wrap_true());
|
||||||
|
}
|
||||||
|
if (frame->func && frame->pc) {
|
||||||
|
Janet *stack = (Janet *)frame + JANET_FRAME_SIZE;
|
||||||
|
JanetArray *slots;
|
||||||
|
off = (int32_t) (frame->pc - def->bytecode);
|
||||||
|
janet_table_put(t, janet_csymbolv(":pc"), janet_wrap_integer(off));
|
||||||
|
if (def->sourcemap) {
|
||||||
|
JanetSourceMapping mapping = def->sourcemap[off];
|
||||||
|
janet_table_put(t, janet_csymbolv(":source-start"), janet_wrap_integer(mapping.start));
|
||||||
|
janet_table_put(t, janet_csymbolv(":source-end"), janet_wrap_integer(mapping.end));
|
||||||
|
}
|
||||||
|
if (def->source) {
|
||||||
|
janet_table_put(t, janet_csymbolv(":source"), janet_wrap_string(def->source));
|
||||||
|
}
|
||||||
|
/* Add stack arguments */
|
||||||
|
slots = janet_array(def->slotcount);
|
||||||
|
memcpy(slots->data, stack, sizeof(Janet) * def->slotcount);
|
||||||
|
slots->count = def->slotcount;
|
||||||
|
janet_table_put(t, janet_csymbolv(":slots"), janet_wrap_array(slots));
|
||||||
|
}
|
||||||
|
return janet_wrap_table(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cfun_stack(JanetArgs args) {
|
||||||
|
JanetFiber *fiber;
|
||||||
|
JanetArray *array;
|
||||||
|
JANET_FIXARITY(args, 1);
|
||||||
|
JANET_ARG_FIBER(fiber, args, 0);
|
||||||
|
array = janet_array(0);
|
||||||
|
{
|
||||||
|
int32_t i = fiber->frame;
|
||||||
|
JanetStackFrame *frame;
|
||||||
|
while (i > 0) {
|
||||||
|
frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||||
|
janet_array_push(array, doframe(frame));
|
||||||
|
i = frame->prevframe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JANET_RETURN_ARRAY(args, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cfun_argstack(JanetArgs args) {
|
||||||
|
JanetFiber *fiber;
|
||||||
|
JanetArray *array;
|
||||||
|
JANET_FIXARITY(args, 1);
|
||||||
|
JANET_ARG_FIBER(fiber, args, 0);
|
||||||
|
array = janet_array(fiber->stacktop - fiber->stackstart);
|
||||||
|
memcpy(array->data, fiber->data + fiber->stackstart, array->capacity * sizeof(Janet));
|
||||||
|
array->count = array->capacity;
|
||||||
|
JANET_RETURN_ARRAY(args, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JanetReg cfuns[] = {
|
||||||
|
{"debug/break", cfun_break,
|
||||||
|
"(debug/break source byte-offset)\n\n"
|
||||||
|
"Sets a breakpoint with source a key at a given byte offset. An offset "
|
||||||
|
"of 0 is the first byte in a file. Will throw an error if the breakpoint location "
|
||||||
|
"cannot be found. For example\n\n"
|
||||||
|
"\t(debug/break \"core.janet\" 1000)\n\n"
|
||||||
|
"wil set a breakpoint at the 1000th byte of the file core.janet."},
|
||||||
|
{"debug/unbreak", cfun_unbreak,
|
||||||
|
"(debug/unbreak source byte-offset)\n\n"
|
||||||
|
"Remove a breakpoint with a source key at a given byte offset. An offset "
|
||||||
|
"of 0 is the first byte in a file. Will throw an error if the breakpoint "
|
||||||
|
"cannot be found."},
|
||||||
|
{"debug/fbreak", cfun_fbreak,
|
||||||
|
"(debug/fbreak fun [,pc=0])\n\n"
|
||||||
|
"Set a breakpoint in a given function. pc is an optional offset, which "
|
||||||
|
"is in bytecode instructions. fun is a function value. Will throw an error "
|
||||||
|
"if the offset is too large or negative."},
|
||||||
|
{"debug/unfbreak", cfun_unfbreak,
|
||||||
|
"(debug/unfbreak fun [,pc=0])\n\n"
|
||||||
|
"Unset a breakpoint set with debug/fbreak."},
|
||||||
|
{"debug/arg-stack", cfun_argstack,
|
||||||
|
"(debug/arg-stack fiber)\n\n"
|
||||||
|
"Gets all values currently on the fiber's argument stack. Normally, "
|
||||||
|
"this should be empty unless the fiber signals while pushing arguments "
|
||||||
|
"to make a function call. Returns a new array."},
|
||||||
|
{"debug/stack", cfun_stack,
|
||||||
|
"(debug/stack fib)\n\n"
|
||||||
|
"Gets information about the stack as an array of tables. Each table "
|
||||||
|
"in the array contains information about a stack frame. The top most, current "
|
||||||
|
"stack frame is the first table in the array, and the bottom most stack frame "
|
||||||
|
"is the last value. Each stack frame contains some of the following attributes:\n\n"
|
||||||
|
"\t:c - true if the stack frame is a c function invocation\n"
|
||||||
|
"\t:column - the current source column of the stack frame\n"
|
||||||
|
"\t:function - the function that the stack frame represents\n"
|
||||||
|
"\t:line - the current source line of the stack frame\n"
|
||||||
|
"\t:name - the human friendly name of the function\n"
|
||||||
|
"\t:pc - integer indicating the location of the program counter\n"
|
||||||
|
"\t:source - string with filename or other identifier for the source code\n"
|
||||||
|
"\t:slots - array of all values in each slot\n"
|
||||||
|
"\t:tail - boolean indicating a tail call"
|
||||||
|
},
|
||||||
|
{"debug/lineage", cfun_lineage,
|
||||||
|
"(debug/lineage fib)\n\n"
|
||||||
|
"Returns an array of all child fibers from a root fiber. This function "
|
||||||
|
"is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
|
||||||
|
"the fiber handling the error can see which fiber raised the signal. This function should "
|
||||||
|
"be used mostly for debugging purposes."
|
||||||
|
},
|
||||||
|
{NULL, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Module entry point */
|
||||||
|
int janet_lib_debug(JanetArgs args) {
|
||||||
|
JanetTable *env = janet_env(args);
|
||||||
|
janet_cfuns(env, NULL, cfuns);
|
||||||
|
return 0;
|
||||||
|
}
|
114
src/core/fiber.c
114
src/core/fiber.c
@ -349,88 +349,11 @@ static int cfun_new(JanetArgs args) {
|
|||||||
|
|
||||||
static int cfun_status(JanetArgs args) {
|
static int cfun_status(JanetArgs args) {
|
||||||
JanetFiber *fiber;
|
JanetFiber *fiber;
|
||||||
const char *status = "";
|
|
||||||
JANET_FIXARITY(args, 1);
|
JANET_FIXARITY(args, 1);
|
||||||
JANET_ARG_FIBER(fiber, args, 0);
|
JANET_ARG_FIBER(fiber, args, 0);
|
||||||
uint32_t s = (fiber->flags & JANET_FIBER_STATUS_MASK) >>
|
uint32_t s = (fiber->flags & JANET_FIBER_STATUS_MASK) >>
|
||||||
JANET_FIBER_STATUS_OFFSET;
|
JANET_FIBER_STATUS_OFFSET;
|
||||||
switch (s) {
|
JANET_RETURN_CSYMBOL(args, janet_status_names[s]);
|
||||||
case JANET_STATUS_DEAD: status = ":dead"; break;
|
|
||||||
case JANET_STATUS_ERROR: status = ":error"; break;
|
|
||||||
case JANET_STATUS_DEBUG: status = ":debug"; break;
|
|
||||||
case JANET_STATUS_PENDING: status = ":pending"; break;
|
|
||||||
case JANET_STATUS_USER0: status = ":user0"; break;
|
|
||||||
case JANET_STATUS_USER1: status = ":user1"; break;
|
|
||||||
case JANET_STATUS_USER2: status = ":user2"; break;
|
|
||||||
case JANET_STATUS_USER3: status = ":user3"; break;
|
|
||||||
case JANET_STATUS_USER4: status = ":user4"; break;
|
|
||||||
case JANET_STATUS_USER5: status = ":user5"; break;
|
|
||||||
case JANET_STATUS_USER6: status = ":user6"; break;
|
|
||||||
case JANET_STATUS_USER7: status = ":user7"; break;
|
|
||||||
case JANET_STATUS_USER8: status = ":user8"; break;
|
|
||||||
case JANET_STATUS_USER9: status = ":user9"; break;
|
|
||||||
case JANET_STATUS_NEW: status = ":new"; break;
|
|
||||||
default:
|
|
||||||
case JANET_STATUS_ALIVE: status = ":alive"; break;
|
|
||||||
}
|
|
||||||
JANET_RETURN_CSYMBOL(args, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract info from one stack frame */
|
|
||||||
static Janet doframe(JanetStackFrame *frame) {
|
|
||||||
int32_t off;
|
|
||||||
JanetTable *t = janet_table(3);
|
|
||||||
JanetFuncDef *def = NULL;
|
|
||||||
if (frame->func) {
|
|
||||||
janet_table_put(t, janet_csymbolv(":function"), janet_wrap_function(frame->func));
|
|
||||||
def = frame->func->def;
|
|
||||||
if (def->name) {
|
|
||||||
janet_table_put(t, janet_csymbolv(":name"), janet_wrap_string(def->name));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
JanetCFunction cfun = (JanetCFunction)(frame->pc);
|
|
||||||
if (cfun) {
|
|
||||||
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
|
|
||||||
if (!janet_checktype(name, JANET_NIL)) {
|
|
||||||
janet_table_put(t, janet_csymbolv(":name"), name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
janet_table_put(t, janet_csymbolv(":c"), janet_wrap_true());
|
|
||||||
}
|
|
||||||
if (frame->flags & JANET_STACKFRAME_TAILCALL) {
|
|
||||||
janet_table_put(t, janet_csymbolv(":tail"), janet_wrap_true());
|
|
||||||
}
|
|
||||||
if (frame->func && frame->pc) {
|
|
||||||
off = (int32_t) (frame->pc - def->bytecode);
|
|
||||||
janet_table_put(t, janet_csymbolv(":pc"), janet_wrap_integer(off));
|
|
||||||
if (def->sourcemap) {
|
|
||||||
JanetSourceMapping mapping = def->sourcemap[off];
|
|
||||||
janet_table_put(t, janet_csymbolv(":line"), janet_wrap_integer(mapping.line));
|
|
||||||
janet_table_put(t, janet_csymbolv(":column"), janet_wrap_integer(mapping.column));
|
|
||||||
}
|
|
||||||
if (def->source) {
|
|
||||||
janet_table_put(t, janet_csymbolv(":source"), janet_wrap_string(def->source));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return janet_wrap_table(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cfun_stack(JanetArgs args) {
|
|
||||||
JanetFiber *fiber;
|
|
||||||
JanetArray *array;
|
|
||||||
JANET_FIXARITY(args, 1);
|
|
||||||
JANET_ARG_FIBER(fiber, args, 0);
|
|
||||||
array = janet_array(0);
|
|
||||||
{
|
|
||||||
int32_t i = fiber->frame;
|
|
||||||
JanetStackFrame *frame;
|
|
||||||
while (i > 0) {
|
|
||||||
frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
|
||||||
janet_array_push(array, doframe(frame));
|
|
||||||
i = frame->prevframe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JANET_RETURN_ARRAY(args, array);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cfun_current(JanetArgs args) {
|
static int cfun_current(JanetArgs args) {
|
||||||
@ -438,19 +361,6 @@ static int cfun_current(JanetArgs args) {
|
|||||||
JANET_RETURN_FIBER(args, janet_vm_fiber);
|
JANET_RETURN_FIBER(args, janet_vm_fiber);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cfun_lineage(JanetArgs args) {
|
|
||||||
JanetFiber *fiber;
|
|
||||||
JanetArray *array;
|
|
||||||
JANET_FIXARITY(args, 1);
|
|
||||||
JANET_ARG_FIBER(fiber, args, 0);
|
|
||||||
array = janet_array(0);
|
|
||||||
while (fiber) {
|
|
||||||
janet_array_push(array, janet_wrap_fiber(fiber));
|
|
||||||
fiber = fiber->child;
|
|
||||||
}
|
|
||||||
JANET_RETURN_ARRAY(args, array);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cfun_maxstack(JanetArgs args) {
|
static int cfun_maxstack(JanetArgs args) {
|
||||||
JanetFiber *fiber;
|
JanetFiber *fiber;
|
||||||
JANET_FIXARITY(args, 1);
|
JANET_FIXARITY(args, 1);
|
||||||
@ -500,32 +410,10 @@ static const JanetReg cfuns[] = {
|
|||||||
"\t:alive - the fiber is currently running and cannot be resumed\n"
|
"\t:alive - the fiber is currently running and cannot be resumed\n"
|
||||||
"\t:new - the fiber has just been created and not yet run"
|
"\t:new - the fiber has just been created and not yet run"
|
||||||
},
|
},
|
||||||
{"fiber/stack", cfun_stack,
|
|
||||||
"(fiber/stack fib)\n\n"
|
|
||||||
"Gets information about the stack as an array of tables. Each table "
|
|
||||||
"in the array contains information about a stack frame. The top most, current "
|
|
||||||
"stack frame is the first table in the array, and the bottom most stack frame "
|
|
||||||
"is the last value. Each stack frame contains some of the following attributes:\n\n"
|
|
||||||
"\t:c - true if the stack frame is a c function invokation\n"
|
|
||||||
"\t:column - the current source column of the stack frame\n"
|
|
||||||
"\t:function - the function that the stack frame represents\n"
|
|
||||||
"\t:line - the current source line of the stack frame\n"
|
|
||||||
"\t:name - the human friendly name of the function\n"
|
|
||||||
"\t:pc - integer indicating the location of the program counter\n"
|
|
||||||
"\t:source - string with filename or other identifier for the source code\n"
|
|
||||||
"\t:tail - boolean indicating a tail call"
|
|
||||||
},
|
|
||||||
{"fiber/current", cfun_current,
|
{"fiber/current", cfun_current,
|
||||||
"(fiber/current)\n\n"
|
"(fiber/current)\n\n"
|
||||||
"Returns the currently running fiber."
|
"Returns the currently running fiber."
|
||||||
},
|
},
|
||||||
{"fiber/lineage", cfun_lineage,
|
|
||||||
"(fiber/lineage fib)\n\n"
|
|
||||||
"Returns an array of all child fibers from a root fiber. This function "
|
|
||||||
"is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
|
|
||||||
"the fiber handling the error can see which fiber raised the signal. This function should "
|
|
||||||
"be used mostly for debugging purposes."
|
|
||||||
},
|
|
||||||
{"fiber/maxstack", cfun_maxstack,
|
{"fiber/maxstack", cfun_maxstack,
|
||||||
"(fiber/maxstack fib)\n\n"
|
"(fiber/maxstack fib)\n\n"
|
||||||
"Gets the maximum stack size in janet values allowed for a fiber. While memory for "
|
"Gets the maximum stack size in janet values allowed for a fiber. While memory for "
|
||||||
|
@ -47,7 +47,7 @@ struct IOFile {
|
|||||||
static int janet_io_gc(void *p, size_t len);
|
static int janet_io_gc(void *p, size_t len);
|
||||||
|
|
||||||
JanetAbstractType janet_io_filetype = {
|
JanetAbstractType janet_io_filetype = {
|
||||||
":core.file",
|
":core/file",
|
||||||
janet_io_gc,
|
janet_io_gc,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -234,8 +234,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
|
|||||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
|
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
|
||||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||||
JanetSourceMapping map = def->sourcemap[i];
|
JanetSourceMapping map = def->sourcemap[i];
|
||||||
pushint(st, map.line);
|
pushint(st, map.start);
|
||||||
pushint(st, map.column);
|
pushint(st, map.end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -740,8 +740,8 @@ static const uint8_t *unmarshal_one_def(
|
|||||||
JANET_OUT_OF_MEMORY;
|
JANET_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
for (int32_t i = 0; i < bytecode_length; i++) {
|
for (int32_t i = 0; i < bytecode_length; i++) {
|
||||||
def->sourcemap[i].line = readint(st, &data);
|
def->sourcemap[i].start = readint(st, &data);
|
||||||
def->sourcemap[i].column = readint(st, &data);
|
def->sourcemap[i].end = readint(st, &data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
def->sourcemap = NULL;
|
def->sourcemap = NULL;
|
||||||
|
@ -102,8 +102,7 @@ struct JanetParseState {
|
|||||||
int32_t counter;
|
int32_t counter;
|
||||||
int32_t argn;
|
int32_t argn;
|
||||||
int flags;
|
int flags;
|
||||||
size_t start_line;
|
size_t start;
|
||||||
size_t start_col;
|
|
||||||
Consumer consumer;
|
Consumer consumer;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -147,8 +146,7 @@ static void pushstate(JanetParser *p, Consumer consumer, int flags) {
|
|||||||
s.argn = 0;
|
s.argn = 0;
|
||||||
s.flags = flags;
|
s.flags = flags;
|
||||||
s.consumer = consumer;
|
s.consumer = consumer;
|
||||||
s.start_line = p->line;
|
s.start = p->offset;
|
||||||
s.start_col = p->col;
|
|
||||||
_pushstate(p, s);
|
_pushstate(p, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,8 +157,8 @@ static void popstate(JanetParser *p, Janet val) {
|
|||||||
if (newtop->flags & PFLAG_CONTAINER) {
|
if (newtop->flags & PFLAG_CONTAINER) {
|
||||||
/* Source mapping info */
|
/* Source mapping info */
|
||||||
if (janet_checktype(val, JANET_TUPLE)) {
|
if (janet_checktype(val, JANET_TUPLE)) {
|
||||||
janet_tuple_sm_line(janet_unwrap_tuple(val)) = (int32_t) top.start_line;
|
janet_tuple_sm_start(janet_unwrap_tuple(val)) = (int32_t) top.start;
|
||||||
janet_tuple_sm_col(janet_unwrap_tuple(val)) = (int32_t) top.start_col;
|
janet_tuple_sm_end(janet_unwrap_tuple(val)) = (int32_t) p->offset;
|
||||||
}
|
}
|
||||||
newtop->argn++;
|
newtop->argn++;
|
||||||
push_arg(p, val);
|
push_arg(p, val);
|
||||||
@ -176,8 +174,8 @@ static void popstate(JanetParser *p, Janet val) {
|
|||||||
t[0] = janet_csymbolv(which);
|
t[0] = janet_csymbolv(which);
|
||||||
t[1] = val;
|
t[1] = val;
|
||||||
/* Quote source mapping info */
|
/* Quote source mapping info */
|
||||||
janet_tuple_sm_line(t) = (int32_t) newtop->start_line;
|
janet_tuple_sm_start(t) = (int32_t) newtop->start;
|
||||||
janet_tuple_sm_col(t) = (int32_t) newtop->start_col;
|
janet_tuple_sm_end(t) = (int32_t) p->offset;
|
||||||
val = janet_wrap_tuple(janet_tuple_end(t));
|
val = janet_wrap_tuple(janet_tuple_end(t));
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
@ -522,12 +520,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||||||
int janet_parser_consume(JanetParser *parser, uint8_t c) {
|
int janet_parser_consume(JanetParser *parser, uint8_t c) {
|
||||||
int consumed = 0;
|
int consumed = 0;
|
||||||
if (parser->error) return 0;
|
if (parser->error) return 0;
|
||||||
if (c == '\n') {
|
parser->offset++;
|
||||||
parser->line++;
|
|
||||||
parser->col = 0;
|
|
||||||
} else if (c != '\r') {
|
|
||||||
parser->col++;
|
|
||||||
}
|
|
||||||
while (!consumed && !parser->error) {
|
while (!consumed && !parser->error) {
|
||||||
JanetParseState *state = parser->states + parser->statecount - 1;
|
JanetParseState *state = parser->states + parser->statecount - 1;
|
||||||
consumed = state->consumer(parser, state, c);
|
consumed = state->consumer(parser, state, c);
|
||||||
@ -584,9 +577,8 @@ void janet_parser_init(JanetParser *parser) {
|
|||||||
parser->statecount = 0;
|
parser->statecount = 0;
|
||||||
parser->statecap = 0;
|
parser->statecap = 0;
|
||||||
parser->error = NULL;
|
parser->error = NULL;
|
||||||
parser->line = 1;
|
|
||||||
parser->col = 0;
|
|
||||||
parser->lookback = -1;
|
parser->lookback = -1;
|
||||||
|
parser->offset = 0;
|
||||||
|
|
||||||
pushstate(parser, root, PFLAG_CONTAINER);
|
pushstate(parser, root, PFLAG_CONTAINER);
|
||||||
}
|
}
|
||||||
@ -617,7 +609,7 @@ static int parsergc(void *p, size_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static JanetAbstractType janet_parse_parsertype = {
|
static JanetAbstractType janet_parse_parsertype = {
|
||||||
":core.parser",
|
":core/parser",
|
||||||
parsergc,
|
parsergc,
|
||||||
parsermark
|
parsermark
|
||||||
};
|
};
|
||||||
@ -742,10 +734,7 @@ static int cfun_where(JanetArgs args) {
|
|||||||
JANET_FIXARITY(args, 1);
|
JANET_FIXARITY(args, 1);
|
||||||
JANET_CHECKABSTRACT(args, 0, &janet_parse_parsertype);
|
JANET_CHECKABSTRACT(args, 0, &janet_parse_parsertype);
|
||||||
p = (JanetParser *) janet_unwrap_abstract(args.v[0]);
|
p = (JanetParser *) janet_unwrap_abstract(args.v[0]);
|
||||||
Janet *tup = janet_tuple_begin(2);
|
JANET_RETURN_INTEGER(args, p->offset);
|
||||||
tup[0] = janet_wrap_integer((int32_t)p->line);
|
|
||||||
tup[1] = janet_wrap_integer((int32_t)p->col);
|
|
||||||
JANET_RETURN_TUPLE(args, janet_tuple_end(tup));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cfun_state(JanetArgs args) {
|
static int cfun_state(JanetArgs args) {
|
||||||
|
@ -67,7 +67,7 @@ void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err) {
|
|||||||
int32_t off = (int32_t) (frame->pc - def->bytecode);
|
int32_t off = (int32_t) (frame->pc - def->bytecode);
|
||||||
if (def->sourcemap) {
|
if (def->sourcemap) {
|
||||||
JanetSourceMapping mapping = def->sourcemap[off];
|
JanetSourceMapping mapping = def->sourcemap[off];
|
||||||
fprintf(stderr, " on line %d, column %d", mapping.line, mapping.column);
|
fprintf(stderr, " at (%d:%d)", mapping.start, mapping.end);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, " pc=%d", off);
|
fprintf(stderr, " pc=%d", off);
|
||||||
}
|
}
|
||||||
@ -75,6 +75,8 @@ void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err) {
|
|||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
janet_v_free(fibers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run a string */
|
/* Run a string */
|
||||||
|
@ -172,6 +172,15 @@ static int destructure(JanetCompiler *c,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Create a source map for definitions. */
|
||||||
|
static const Janet *janetc_make_sourcemap(JanetCompiler *c) {
|
||||||
|
Janet *tup = janet_tuple_begin(3);
|
||||||
|
tup[0] = janet_wrap_string(c->source);
|
||||||
|
tup[1] = janet_wrap_integer(c->current_mapping.start);
|
||||||
|
tup[2] = janet_wrap_integer(c->current_mapping.end);
|
||||||
|
return janet_tuple_end(tup);
|
||||||
|
}
|
||||||
|
|
||||||
static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) {
|
static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||||
/*JanetFopts subopts = janetc_fopts_default(opts.compiler);*/
|
/*JanetFopts subopts = janetc_fopts_default(opts.compiler);*/
|
||||||
/*JanetSlot ret, dest;*/
|
/*JanetSlot ret, dest;*/
|
||||||
@ -252,6 +261,8 @@ static int varleaf(
|
|||||||
JanetArray *ref = janet_array(1);
|
JanetArray *ref = janet_array(1);
|
||||||
janet_array_push(ref, janet_wrap_nil());
|
janet_array_push(ref, janet_wrap_nil());
|
||||||
janet_table_put(reftab, janet_csymbolv(":ref"), janet_wrap_array(ref));
|
janet_table_put(reftab, janet_csymbolv(":ref"), janet_wrap_array(ref));
|
||||||
|
janet_table_put(reftab, janet_csymbolv(":source-map"),
|
||||||
|
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(reftab));
|
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(reftab));
|
||||||
refslot = janetc_cslot(janet_wrap_array(ref));
|
refslot = janetc_cslot(janet_wrap_array(ref));
|
||||||
janetc_emit_ssu(c, JOP_PUT_INDEX, refslot, s, 0, 0);
|
janetc_emit_ssu(c, JOP_PUT_INDEX, refslot, s, 0, 0);
|
||||||
@ -278,6 +289,8 @@ static int defleaf(
|
|||||||
JanetTable *attr) {
|
JanetTable *attr) {
|
||||||
if (c->scope->flags & JANET_SCOPE_TOP) {
|
if (c->scope->flags & JANET_SCOPE_TOP) {
|
||||||
JanetTable *tab = janet_table(2);
|
JanetTable *tab = janet_table(2);
|
||||||
|
janet_table_put(tab, janet_csymbolv(":source-map"),
|
||||||
|
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||||
tab->proto = attr;
|
tab->proto = attr;
|
||||||
JanetSlot valsym = janetc_cslot(janet_csymbolv(":value"));
|
JanetSlot valsym = janetc_cslot(janet_csymbolv(":value"));
|
||||||
JanetSlot tabslot = janetc_cslot(janet_wrap_table(tab));
|
JanetSlot tabslot = janetc_cslot(janet_wrap_table(tab));
|
||||||
@ -648,16 +661,15 @@ error2:
|
|||||||
|
|
||||||
/* Keep in lexicographic order */
|
/* Keep in lexicographic order */
|
||||||
static const JanetSpecial janetc_specials[] = {
|
static const JanetSpecial janetc_specials[] = {
|
||||||
{":=", janetc_varset},
|
|
||||||
{"def", janetc_def},
|
{"def", janetc_def},
|
||||||
{"do", janetc_do},
|
{"do", janetc_do},
|
||||||
{"fn", janetc_fn},
|
{"fn", janetc_fn},
|
||||||
{"if", janetc_if},
|
{"if", janetc_if},
|
||||||
{"quasiquote", janetc_quasiquote},
|
{"quasiquote", janetc_quasiquote},
|
||||||
{"quote", janetc_quote},
|
{"quote", janetc_quote},
|
||||||
|
{"set", janetc_varset},
|
||||||
{"splice", janetc_splice},
|
{"splice", janetc_splice},
|
||||||
{"unquote", janetc_unquote},
|
{"unquote", janetc_unquote},
|
||||||
{"unquote", janetc_unquote},
|
|
||||||
{"var", janetc_var},
|
{"var", janetc_var},
|
||||||
{"while", janetc_while}
|
{"while", janetc_while}
|
||||||
};
|
};
|
||||||
|
@ -32,8 +32,8 @@ Janet *janet_tuple_begin(int32_t length) {
|
|||||||
char *data = janet_gcalloc(JANET_MEMORY_TUPLE, 4 * sizeof(int32_t) + length * sizeof(Janet));
|
char *data = janet_gcalloc(JANET_MEMORY_TUPLE, 4 * sizeof(int32_t) + length * sizeof(Janet));
|
||||||
Janet *tuple = (Janet *)(data + (4 * sizeof(int32_t)));
|
Janet *tuple = (Janet *)(data + (4 * sizeof(int32_t)));
|
||||||
janet_tuple_length(tuple) = length;
|
janet_tuple_length(tuple) = length;
|
||||||
janet_tuple_sm_line(tuple) = 0;
|
janet_tuple_sm_start(tuple) = -1;
|
||||||
janet_tuple_sm_col(tuple) = 0;
|
janet_tuple_sm_end(tuple) = -1;
|
||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,42 @@ const char *const janet_type_names[16] = {
|
|||||||
":abstract"
|
":abstract"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char *const janet_signal_names[14] = {
|
||||||
|
":ok",
|
||||||
|
":error",
|
||||||
|
":debug",
|
||||||
|
":yield",
|
||||||
|
":user0",
|
||||||
|
":user1",
|
||||||
|
":user2",
|
||||||
|
":user3",
|
||||||
|
":user4",
|
||||||
|
":user5",
|
||||||
|
":user6",
|
||||||
|
":user7",
|
||||||
|
":user8",
|
||||||
|
":user9"
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *const janet_status_names[16] = {
|
||||||
|
":dead",
|
||||||
|
":error",
|
||||||
|
":debug",
|
||||||
|
":pending",
|
||||||
|
":user0",
|
||||||
|
":user1",
|
||||||
|
":user2",
|
||||||
|
":user3",
|
||||||
|
":user4",
|
||||||
|
":user5",
|
||||||
|
":user6",
|
||||||
|
":user7",
|
||||||
|
":user8",
|
||||||
|
":user9",
|
||||||
|
":new",
|
||||||
|
":alive"
|
||||||
|
};
|
||||||
|
|
||||||
/* Calculate hash for string */
|
/* Calculate hash for string */
|
||||||
|
|
||||||
int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
|
int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
|
||||||
|
@ -57,5 +57,6 @@ int janet_lib_parse(JanetArgs args);
|
|||||||
int janet_lib_asm(JanetArgs args);
|
int janet_lib_asm(JanetArgs args);
|
||||||
#endif
|
#endif
|
||||||
int janet_lib_compile(JanetArgs args);
|
int janet_lib_compile(JanetArgs args);
|
||||||
|
int janet_lib_debug(JanetArgs args);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -54,6 +54,8 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
|
|||||||
/* Expected types on type error */
|
/* Expected types on type error */
|
||||||
uint16_t expected_types;
|
uint16_t expected_types;
|
||||||
|
|
||||||
|
uint8_t first_opcode;
|
||||||
|
|
||||||
/* Signal to return when done */
|
/* Signal to return when done */
|
||||||
JanetSignal signal = JANET_SIGNAL_OK;
|
JanetSignal signal = JANET_SIGNAL_OK;
|
||||||
|
|
||||||
@ -92,16 +94,24 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
|
|||||||
* instruction. */
|
* instruction. */
|
||||||
retreg = in;
|
retreg = in;
|
||||||
goto vm_resume_child;
|
goto vm_resume_child;
|
||||||
} else if (startstatus != JANET_STATUS_NEW) {
|
} else if (startstatus != JANET_STATUS_NEW &&
|
||||||
|
((*pc & 0xFF) == JOP_SIGNAL)) {
|
||||||
/* Only should be hit if child is waiting on a SIGNAL instruction */
|
/* Only should be hit if child is waiting on a SIGNAL instruction */
|
||||||
/* If waiting for response to signal, use input and increment pc */
|
/* If waiting for response to signal, use input and increment pc */
|
||||||
stack[oparg(1, 0xFF)] = in;
|
stack[oparg(1, 0xFF)] = in;
|
||||||
pc++;
|
pc++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The first opcode to execute. If the first opcode has
|
||||||
|
* the breakpoint bit set and we were in the debug state, skip
|
||||||
|
* that first breakpoint. */
|
||||||
|
first_opcode = (startstatus == JANET_STATUS_DEBUG)
|
||||||
|
? (*pc & 0x7F)
|
||||||
|
: (*pc & 0xFF);
|
||||||
|
|
||||||
/* Use computed gotos for GCC and clang, otherwise use switch */
|
/* Use computed gotos for GCC and clang, otherwise use switch */
|
||||||
#ifdef __GNUC__
|
#ifdef ____GNUC__
|
||||||
#define VM_START() {vm_next();
|
#define VM_START() { goto *op_lookup[first_opcode];
|
||||||
#define VM_END() }
|
#define VM_END() }
|
||||||
#define VM_OP(op) label_##op :
|
#define VM_OP(op) label_##op :
|
||||||
#define VM_DEFAULT() label_unknown_op:
|
#define VM_DEFAULT() label_unknown_op:
|
||||||
@ -193,11 +203,11 @@ static void *op_lookup[255] = {
|
|||||||
&&label_unknown_op
|
&&label_unknown_op
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
#define VM_START() for(;;){switch(*pc & 0xFF){
|
#define VM_START() uint8_t opcode = first_opcode; for (;;) {switch(opcode) {
|
||||||
#define VM_END() }}
|
#define VM_END() }}
|
||||||
#define VM_OP(op) case op :
|
#define VM_OP(op) case op :
|
||||||
#define VM_DEFAULT() default:
|
#define VM_DEFAULT() default:
|
||||||
#define vm_next() continue
|
#define vm_next() opcode = *pc & 0xFF; continue
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define vm_checkgc_next() janet_maybe_collect(); vm_next()
|
#define vm_checkgc_next() janet_maybe_collect(); vm_next()
|
||||||
@ -279,6 +289,7 @@ static void *op_lookup[255] = {
|
|||||||
VM_START();
|
VM_START();
|
||||||
|
|
||||||
VM_DEFAULT();
|
VM_DEFAULT();
|
||||||
|
signal = JANET_SIGNAL_DEBUG;
|
||||||
retreg = janet_wrap_nil();
|
retreg = janet_wrap_nil();
|
||||||
goto vm_exit;
|
goto vm_exit;
|
||||||
|
|
||||||
@ -535,7 +546,6 @@ static void *op_lookup[255] = {
|
|||||||
pc++;
|
pc++;
|
||||||
vm_next();
|
vm_next();
|
||||||
|
|
||||||
/* Candidate */
|
|
||||||
VM_OP(JOP_GREATER_THAN_INTEGER)
|
VM_OP(JOP_GREATER_THAN_INTEGER)
|
||||||
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
||||||
janet_unwrap_integer(stack[oparg(2, 0xFF)]) >
|
janet_unwrap_integer(stack[oparg(2, 0xFF)]) >
|
||||||
@ -543,7 +553,6 @@ static void *op_lookup[255] = {
|
|||||||
pc++;
|
pc++;
|
||||||
vm_next();
|
vm_next();
|
||||||
|
|
||||||
/* Candidate */
|
|
||||||
VM_OP(JOP_GREATER_THAN_IMMEDIATE)
|
VM_OP(JOP_GREATER_THAN_IMMEDIATE)
|
||||||
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
||||||
janet_unwrap_integer(stack[oparg(2, 0xFF)]) > ((*(int32_t *)pc) >> 24)
|
janet_unwrap_integer(stack[oparg(2, 0xFF)]) > ((*(int32_t *)pc) >> 24)
|
||||||
@ -551,7 +560,6 @@ static void *op_lookup[255] = {
|
|||||||
pc++;
|
pc++;
|
||||||
vm_next();
|
vm_next();
|
||||||
|
|
||||||
/* Candidate */
|
|
||||||
VM_OP(JOP_GREATER_THAN_REAL)
|
VM_OP(JOP_GREATER_THAN_REAL)
|
||||||
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
||||||
janet_unwrap_real(stack[oparg(2, 0xFF)]) >
|
janet_unwrap_real(stack[oparg(2, 0xFF)]) >
|
||||||
@ -559,7 +567,6 @@ static void *op_lookup[255] = {
|
|||||||
pc++;
|
pc++;
|
||||||
vm_next();
|
vm_next();
|
||||||
|
|
||||||
/* Candidate */
|
|
||||||
VM_OP(JOP_GREATER_THAN_EQUAL_REAL)
|
VM_OP(JOP_GREATER_THAN_EQUAL_REAL)
|
||||||
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
||||||
janet_unwrap_real(stack[oparg(2, 0xFF)]) >=
|
janet_unwrap_real(stack[oparg(2, 0xFF)]) >=
|
||||||
@ -575,7 +582,6 @@ static void *op_lookup[255] = {
|
|||||||
pc++;
|
pc++;
|
||||||
vm_next();
|
vm_next();
|
||||||
|
|
||||||
/* Candidate */
|
|
||||||
VM_OP(JOP_EQUALS_INTEGER)
|
VM_OP(JOP_EQUALS_INTEGER)
|
||||||
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
||||||
janet_unwrap_integer(stack[oparg(2, 0xFF)]) ==
|
janet_unwrap_integer(stack[oparg(2, 0xFF)]) ==
|
||||||
@ -584,7 +590,6 @@ static void *op_lookup[255] = {
|
|||||||
pc++;
|
pc++;
|
||||||
vm_next();
|
vm_next();
|
||||||
|
|
||||||
/* Candidate */
|
|
||||||
VM_OP(JOP_EQUALS_REAL)
|
VM_OP(JOP_EQUALS_REAL)
|
||||||
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
||||||
janet_unwrap_real(stack[oparg(2, 0xFF)]) ==
|
janet_unwrap_real(stack[oparg(2, 0xFF)]) ==
|
||||||
@ -593,7 +598,6 @@ static void *op_lookup[255] = {
|
|||||||
pc++;
|
pc++;
|
||||||
vm_next();
|
vm_next();
|
||||||
|
|
||||||
/* Candidate */
|
|
||||||
VM_OP(JOP_EQUALS_IMMEDIATE)
|
VM_OP(JOP_EQUALS_IMMEDIATE)
|
||||||
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
stack[oparg(1, 0xFF)] = janet_wrap_boolean(
|
||||||
janet_unwrap_integer(stack[oparg(2, 0xFF)]) == ((*(int32_t *)pc) >> 24)
|
janet_unwrap_integer(stack[oparg(2, 0xFF)]) == ((*(int32_t *)pc) >> 24)
|
||||||
|
@ -29,7 +29,7 @@ extern "C" {
|
|||||||
|
|
||||||
/***** START SECTION CONFIG *****/
|
/***** START SECTION CONFIG *****/
|
||||||
|
|
||||||
#define JANET_VERSION "0.1.0"
|
#define JANET_VERSION "0.2.0"
|
||||||
|
|
||||||
#ifndef JANET_BUILD
|
#ifndef JANET_BUILD
|
||||||
#define JANET_BUILD "local"
|
#define JANET_BUILD "local"
|
||||||
@ -204,6 +204,8 @@ extern "C" {
|
|||||||
|
|
||||||
/* Names of all of the types */
|
/* Names of all of the types */
|
||||||
extern const char *const janet_type_names[16];
|
extern const char *const janet_type_names[16];
|
||||||
|
extern const char *const janet_signal_names[14];
|
||||||
|
extern const char *const janet_status_names[16];
|
||||||
|
|
||||||
/* Fiber signals */
|
/* Fiber signals */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -667,8 +669,8 @@ struct JanetKV {
|
|||||||
|
|
||||||
/* Source mapping structure for a bytecode instruction */
|
/* Source mapping structure for a bytecode instruction */
|
||||||
struct JanetSourceMapping {
|
struct JanetSourceMapping {
|
||||||
int32_t line;
|
int32_t start;
|
||||||
int32_t column;
|
int32_t end;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A function definition. Contains information needed to instantiate closures. */
|
/* A function definition. Contains information needed to instantiate closures. */
|
||||||
@ -731,8 +733,7 @@ struct JanetParser {
|
|||||||
size_t statecap;
|
size_t statecap;
|
||||||
size_t bufcount;
|
size_t bufcount;
|
||||||
size_t bufcap;
|
size_t bufcap;
|
||||||
size_t line;
|
size_t offset;
|
||||||
size_t col;
|
|
||||||
int lookback;
|
int lookback;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -937,6 +938,13 @@ JANET_API Janet janet_scan_number(const uint8_t *src, int32_t len);
|
|||||||
JANET_API int32_t janet_scan_integer(const uint8_t *str, int32_t len, int *err);
|
JANET_API int32_t janet_scan_integer(const uint8_t *str, int32_t len, int *err);
|
||||||
JANET_API double janet_scan_real(const uint8_t *str, int32_t len, int *err);
|
JANET_API double janet_scan_real(const uint8_t *str, int32_t len, int *err);
|
||||||
|
|
||||||
|
/* Debugging */
|
||||||
|
JANET_API int janet_debug_break(JanetFuncDef *def, int32_t pc);
|
||||||
|
JANET_API int janet_debug_unbreak(JanetFuncDef *def, int32_t pc);
|
||||||
|
JANET_API int janet_debug_find(
|
||||||
|
JanetFuncDef **def_out, int32_t *pc_out,
|
||||||
|
const uint8_t *source, int32_t offset);
|
||||||
|
|
||||||
/* Array functions */
|
/* Array functions */
|
||||||
JANET_API JanetArray *janet_array(int32_t capacity);
|
JANET_API JanetArray *janet_array(int32_t capacity);
|
||||||
JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n);
|
JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n);
|
||||||
@ -967,8 +975,8 @@ JANET_API int janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x);
|
|||||||
#define janet_tuple_raw(t) ((int32_t *)(t) - 4)
|
#define janet_tuple_raw(t) ((int32_t *)(t) - 4)
|
||||||
#define janet_tuple_length(t) (janet_tuple_raw(t)[0])
|
#define janet_tuple_length(t) (janet_tuple_raw(t)[0])
|
||||||
#define janet_tuple_hash(t) ((janet_tuple_raw(t)[1]))
|
#define janet_tuple_hash(t) ((janet_tuple_raw(t)[1]))
|
||||||
#define janet_tuple_sm_line(t) ((janet_tuple_raw(t)[2]))
|
#define janet_tuple_sm_start(t) ((janet_tuple_raw(t)[2]))
|
||||||
#define janet_tuple_sm_col(t) ((janet_tuple_raw(t)[3]))
|
#define janet_tuple_sm_end(t) ((janet_tuple_raw(t)[3]))
|
||||||
JANET_API Janet *janet_tuple_begin(int32_t length);
|
JANET_API Janet *janet_tuple_begin(int32_t length);
|
||||||
JANET_API const Janet *janet_tuple_end(Janet *tuple);
|
JANET_API const Janet *janet_tuple_end(Janet *tuple);
|
||||||
JANET_API const Janet *janet_tuple_n(const Janet *values, int32_t n);
|
JANET_API const Janet *janet_tuple_n(const Janet *values, int32_t n);
|
||||||
|
@ -24,12 +24,12 @@
|
|||||||
(os/exit 0)
|
(os/exit 0)
|
||||||
1)
|
1)
|
||||||
"v" (fn [&] (print janet/version "-" janet/build) (os/exit 0) 1)
|
"v" (fn [&] (print janet/version "-" janet/build) (os/exit 0) 1)
|
||||||
"s" (fn [&] (:= *raw-stdin* true) (:= *should-repl* true) 1)
|
"s" (fn [&] (set *raw-stdin* true) (set *should-repl* true) 1)
|
||||||
"r" (fn [&] (:= *should-repl* true) 1)
|
"r" (fn [&] (set *should-repl* true) 1)
|
||||||
"p" (fn [&] (:= *exit-on-error* false) 1)
|
"p" (fn [&] (set *exit-on-error* false) 1)
|
||||||
"-" (fn [&] (:= *handleopts* false) 1)
|
"-" (fn [&] (set *handleopts* false) 1)
|
||||||
"e" (fn [i &]
|
"e" (fn [i &]
|
||||||
(:= *no-file* false)
|
(set *no-file* false)
|
||||||
(eval (get process/args (+ i 1)))
|
(eval (get process/args (+ i 1)))
|
||||||
2)})
|
2)})
|
||||||
|
|
||||||
@ -45,7 +45,7 @@
|
|||||||
(if (and *handleopts* (= "-" (string/slice arg 0 1)))
|
(if (and *handleopts* (= "-" (string/slice arg 0 1)))
|
||||||
(+= i (dohandler (string/slice arg 1 2) i))
|
(+= i (dohandler (string/slice arg 1 2) i))
|
||||||
(do
|
(do
|
||||||
(:= *no-file* false)
|
(set *no-file* false)
|
||||||
(import* _env arg :prefix "" :exit *exit-on-error*)
|
(import* _env arg :prefix "" :exit *exit-on-error*)
|
||||||
(++ i))))
|
(++ i))))
|
||||||
|
|
||||||
@ -55,6 +55,6 @@
|
|||||||
(do
|
(do
|
||||||
(print (string "Janet " janet/version "-" janet/build " Copyright (C) 2017-2018 Calvin Rose"))
|
(print (string "Janet " janet/version "-" janet/build " Copyright (C) 2017-2018 Calvin Rose"))
|
||||||
(repl (fn [buf p]
|
(repl (fn [buf p]
|
||||||
(def [line] (parser/where p))
|
(def offset (parser/where p))
|
||||||
(def prompt (string "janet:" line ":" (parser/state p) "> "))
|
(def prompt (string "janet:" offset ":" (parser/state p) "> "))
|
||||||
(getline prompt buf)))))))
|
(getline prompt buf)))))))
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
(fiber/new (fn webrepl []
|
(fiber/new (fn webrepl []
|
||||||
(repl (fn get-line [buf p]
|
(repl (fn get-line [buf p]
|
||||||
(def [line] (parser/where p))
|
(def offset (parser/where p))
|
||||||
(def prompt (string "janet:" line ":" (parser/state p) "> "))
|
(def prompt (string "janet:" offset ":" (parser/state p) "> "))
|
||||||
(repl-yield prompt buf)
|
(repl-yield prompt buf)
|
||||||
(yield)
|
(yield)
|
||||||
buf))))
|
buf))))
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
x)
|
x)
|
||||||
|
|
||||||
(defn start-suite [x]
|
(defn start-suite [x]
|
||||||
(:= suite-num x)
|
(set suite-num x)
|
||||||
(print "\nRunning test suite " x " tests...\n"))
|
(print "\nRunning test suite " x " tests...\n"))
|
||||||
|
|
||||||
(defn end-suite []
|
(defn end-suite []
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
(assert (= 10 (+ 1 2 3 4)) "addition")
|
(assert (= 10 (+ 1 2 3 4)) "addition")
|
||||||
(assert (= -8 (- 1 2 3 4)) "subtraction")
|
(assert (= -8 (- 1 2 3 4)) "subtraction")
|
||||||
(assert (= 24 (* 1 2 3 4)) "multiplication")
|
(assert (= 24 (* 1 2 3 4)) "multiplication")
|
||||||
(assert (= 4 (<< 1 2)) "left shift")
|
(assert (= 4 (blshift 1 2)) "left shift")
|
||||||
(assert (= 1 (>> 4 2)) "right shift")
|
(assert (= 1 (brshift 4 2)) "right shift")
|
||||||
(assert (< 1 2 3 4 5 6) "less than integers")
|
(assert (< 1 2 3 4 5 6) "less than integers")
|
||||||
(assert (< 1.0 2.0 3.0 4.0 5.0 6.0) "less than reals")
|
(assert (< 1.0 2.0 3.0 4.0 5.0 6.0) "less than reals")
|
||||||
(assert (> 6 5 4 3 2 1) "greater than integers")
|
(assert (> 6 5 4 3 2 1) "greater than integers")
|
||||||
@ -60,10 +60,10 @@
|
|||||||
(assert (not false) "false literal")
|
(assert (not false) "false literal")
|
||||||
(assert true "true literal")
|
(assert true "true literal")
|
||||||
(assert (not nil) "nil literal")
|
(assert (not nil) "nil literal")
|
||||||
(assert (= 7 (| 3 4)) "bit or")
|
(assert (= 7 (bor 3 4)) "bit or")
|
||||||
(assert (= 0 (& 3 4)) "bit and")
|
(assert (= 0 (band 3 4)) "bit and")
|
||||||
(assert (= 0xFF (^ 0x0F 0xF0)) "bit xor")
|
(assert (= 0xFF (bxor 0x0F 0xF0)) "bit xor")
|
||||||
(assert (= 0xF0 (^ 0xFF 0x0F)) "bit xor 2")
|
(assert (= 0xF0 (bxor 0xFF 0x0F)) "bit xor 2")
|
||||||
|
|
||||||
# Set global variables to prevent some possible compiler optimizations that defeat point of the test
|
# Set global variables to prevent some possible compiler optimizations that defeat point of the test
|
||||||
(var zero 0)
|
(var zero 0)
|
||||||
@ -80,7 +80,7 @@
|
|||||||
|
|
||||||
# Mcarthy's 91 function
|
# Mcarthy's 91 function
|
||||||
(var f91 nil)
|
(var f91 nil)
|
||||||
(:= f91 (fn [n] (if (> n 100) (- n 10) (f91 (f91 (+ n 11))))))
|
(set f91 (fn [n] (if (> n 100) (- n 10) (f91 (f91 (+ n 11))))))
|
||||||
(assert (= 91 (f91 10)) "f91(10) = 91")
|
(assert (= 91 (f91 10)) "f91(10) = 91")
|
||||||
(assert (= 91 (f91 11)) "f91(11) = 91")
|
(assert (= 91 (f91 11)) "f91(11) = 91")
|
||||||
(assert (= 91 (f91 20)) "f91(20) = 91")
|
(assert (= 91 (f91 20)) "f91(20) = 91")
|
||||||
@ -92,7 +92,7 @@
|
|||||||
(assert (= 94 (f91 104)) "f91(104) = 94")
|
(assert (= 94 (f91 104)) "f91(104) = 94")
|
||||||
|
|
||||||
# Fibonacci
|
# Fibonacci
|
||||||
(def fib (do (var fib nil) (:= fib (fn [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))))))
|
(def fib (do (var fib nil) (set fib (fn [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))))))
|
||||||
(def fib2 (fn fib2 [n] (if (< n 2) n (+ (fib2 (- n 1)) (fib2 (- n 2))))))
|
(def fib2 (fn fib2 [n] (if (< n 2) n (+ (fib2 (- n 1)) (fib2 (- n 2))))))
|
||||||
|
|
||||||
(assert (= (fib 0) (fib2 0) 0) "fib(0)")
|
(assert (= (fib 0) (fib2 0) 0) "fib(0)")
|
||||||
@ -127,15 +127,15 @@
|
|||||||
(var accum 1)
|
(var accum 1)
|
||||||
(var count 0)
|
(var count 0)
|
||||||
(while (< count 16)
|
(while (< count 16)
|
||||||
(:= accum (<< accum 1))
|
(set accum (blshift accum 1))
|
||||||
(:= count (+ 1 count)))
|
(set count (+ 1 count)))
|
||||||
(assert (= accum 65536) "loop in closure")))
|
(assert (= accum 65536) "loop in closure")))
|
||||||
|
|
||||||
(var accum 1)
|
(var accum 1)
|
||||||
(var count 0)
|
(var count 0)
|
||||||
(while (< count 16)
|
(while (< count 16)
|
||||||
(:= accum (<< accum 1))
|
(set accum (blshift accum 1))
|
||||||
(:= count (+ 1 count)))
|
(set count (+ 1 count)))
|
||||||
(assert (= accum 65536) "loop globally")
|
(assert (= accum 65536) "loop globally")
|
||||||
|
|
||||||
(assert (= (struct 1 2 3 4 5 6 7 8) (struct 7 8 5 6 3 4 1 2)) "struct order does not matter 1")
|
(assert (= (struct 1 2 3 4 5 6 7 8) (struct 7 8 5 6 3 4 1 2)) "struct order does not matter 1")
|
||||||
@ -228,18 +228,18 @@
|
|||||||
(def xi (get xs i))
|
(def xi (get xs i))
|
||||||
(def yj (get ys j))
|
(def yj (get ys j))
|
||||||
(if (< xi yj)
|
(if (< xi yj)
|
||||||
(do (array/push ret xi) (:= i (+ i 1)))
|
(do (array/push ret xi) (set i (+ i 1)))
|
||||||
(do (array/push ret yj) (:= j (+ j 1)))))
|
(do (array/push ret yj) (set j (+ j 1)))))
|
||||||
# Push rest of xs
|
# Push rest of xs
|
||||||
(while (< i xlen)
|
(while (< i xlen)
|
||||||
(def xi (get xs i))
|
(def xi (get xs i))
|
||||||
(array/push ret xi)
|
(array/push ret xi)
|
||||||
(:= i (+ i 1)))
|
(set i (+ i 1)))
|
||||||
# Push rest of ys
|
# Push rest of ys
|
||||||
(while (< j ylen)
|
(while (< j ylen)
|
||||||
(def yj (get ys j))
|
(def yj (get ys j))
|
||||||
(array/push ret yj)
|
(array/push ret yj)
|
||||||
(:= j (+ j 1)))
|
(set j (+ j 1)))
|
||||||
ret)
|
ret)
|
||||||
|
|
||||||
(assert (apply <= (merge @[1 3 5] @[2 4 6])) "merge sort merge 1")
|
(assert (apply <= (merge @[1 3 5] @[2 4 6])) "merge sort merge 1")
|
||||||
@ -255,7 +255,7 @@
|
|||||||
(var count 0)
|
(var count 0)
|
||||||
(while (< count 128)
|
(while (< count 128)
|
||||||
(put syms (gensym) true)
|
(put syms (gensym) true)
|
||||||
(:= count (+ 1 count)))
|
(set count (+ 1 count)))
|
||||||
(assert (= (length syms) 128) "many symbols")))
|
(assert (= (length syms) 128) "many symbols")))
|
||||||
|
|
||||||
# Let
|
# Let
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
(defn myfun [x]
|
(defn myfun [x]
|
||||||
(var a 10)
|
(var a 10)
|
||||||
(:= a (do
|
(set a (do
|
||||||
(def y x)
|
(def y x)
|
||||||
(if x 8 9))))
|
(if x 8 9))))
|
||||||
|
|
||||||
@ -44,7 +44,7 @@
|
|||||||
(var good true)
|
(var good true)
|
||||||
(loop [i :range [0 n]]
|
(loop [i :range [0 n]]
|
||||||
(if (not (f))
|
(if (not (f))
|
||||||
(:= good false)))
|
(set good false)))
|
||||||
(assert good e))
|
(assert good e))
|
||||||
|
|
||||||
(assert-many (fn [] (>= 1 (math/random) 0)) 200 "(random) between 0 and 1")
|
(assert-many (fn [] (>= 1 (math/random) 0)) 200 "(random) between 0 and 1")
|
||||||
|
Loading…
Reference in New Issue
Block a user