aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md15
-rw-r--r--spec.md36
-rw-r--r--src/Interpreter.hs5
-rw-r--r--test/Spec.hs10
-rw-r--r--test/scripts/record1.milch15
5 files changed, 55 insertions, 26 deletions
diff --git a/README.md b/README.md
index 056ef96..4a3538e 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
## Summary
-`MILCH` (or `Milch`, or `milch`) is a interpreted, dynamically typed functional programming language that has some Lisp-inspired syntax and Haskell-inspired semantics. Milch is german for 🥛.
+`MILCH` (or `Milch`, or `milch`) is a interpreted, dynamically typed functional programming language that has some Lisp-inspired syntax and Haskell-inspired semantics. Milch is German for 🥛.
I'm developing this language by myself as a hobby, and the spec is going to change all the time. Some features documented here might not yet be implemented.
@@ -14,18 +14,19 @@ Check out the TODO
## Building
-Install a Haskell toolchain with Stack and GHC. Run
+Install a Haskell toolchain with Stack and GHC. To build and install the `milch` binary in your `PATH`, run:
- stack build
stack install
-to build and install the `milch` binary in your `PATH`.
-
-Then, run
+To see the help text, run:
milch -h
-to see the help text.
+## Tests
+
+To run the test suite, run:
+
+ stack test
## Author
diff --git a/spec.md b/spec.md
index 953b672..934e119 100644
--- a/spec.md
+++ b/spec.md
@@ -1,28 +1,36 @@
# Language spec
- Dynamically and weakly typed, interpreted
-- Impure functions marked with ! (convention)
+- Impure functions marked with ! (convention), purity is tracked by the interpreter
- Boolean functions marked with ? (convention)
- Loops implemented with recursion
## Value types
- Number -3.14
- Symbol PI
- Boolean true
- String "foobar"
- Vector [1 2 3]
- Hash map { a 1 b 2 }
- Function (\[x y] (+ x y))
+ Number -3.14
+ Symbol PI
+ Tag :tag
+ Boolean true
+ String "foobar"
+ Vector [1 2 3]
+ Hash map { a 1 b 2 }
+ Function (\[x y] (+ x y))
## Syntax
### Function calls
-A sequence of values surrounded by parens is considered a function call In a function call, the first element must evaluate to a function value. The function will be invoked with the rest of the elements as arguments to the function.
+A sequence of values surrounded by parens is considered a function call In a function call, the first element must evaluate to a function value. The function will be invoked with the rest of the elements as arguments to the function. A function call with no arguments evaluates to the function itself.
### Function definition
-Calling the builtin variadic function '\' constructs a new function. The first argument must be a vector of symbols (parameter list). The rest of the arguments form the function body. The evaluated value of the last argument is returned as the return value of the function.
+
+Calling the builtin variadic function `\` constructs a new function. The first argument must be a vector of symbols (parameter list). The rest of the arguments form the function body. The evaluated value of the last argument is returned as the return value of the function.
+
+Memoized pure functions can be created with a special `let memo`-binding:
+
+ (let memo f (\[x] x))
+
+Impure functions can be created with the builtin `\!`.
### Currying
@@ -43,20 +51,20 @@ Evaluates second argument and stores the resulting value in the environment.
`\`
Defines a function.
- (\[x y] + x y)
+ (\[x y] (+ x y))
`print!`
-Prints a value and a newline.
+Prints a value without a newline.
(print! PI)
`match`
-Matches first argument (value) to even positioned arguments (matchers). On match (at position i), evaluates the odd positioned argument (branch) right after it (at position i + 1). If given an even number of arguments, the last argument will act as the default branch.
+Matches first argument (value) to even positioned arguments (matchers). On match (at position i), evaluates the odd positioned argument (branch) right after it (at position i + 1).
(match (sum2 1 1)
2 (do-thing)
3 (do-other-thing)
- (do-else))
+ otherwise (do-else))
`head`
Gets the first element of a vector.
diff --git a/src/Interpreter.hs b/src/Interpreter.hs
index c5c19e0..ca87a82 100644
--- a/src/Interpreter.hs
+++ b/src/Interpreter.hs
@@ -20,9 +20,7 @@ import Tokenizer ( tokenize' )
import Parser ( parse )
curryCall :: [AST] -> ASTNode -> LContext AST
-curryCall [] (ASTFunction fIsPure f) = do
- checkPurity fIsPure
- f (makeNonsenseAST ASTUnit)
+curryCall [] astNode = return $ makeNonsenseAST astNode
curryCall (arg:[]) (ASTFunction fIsPure f) = do
checkPurity fIsPure
f arg
@@ -33,7 +31,6 @@ curryCall (arg:rest) f = do
checkPurity fIsPure
f' arg
other -> throwL (astPos g) $ "cannot call value " ++ show other ++ " as a function"
-curryCall _ astFn = throwL "" $ "unreachable: curryCall, astFn: " ++ show astFn
traverseAndReplace :: String -> AST -> AST -> AST
traverseAndReplace param arg ast@AST { an = ASTSymbol sym }
diff --git a/test/Spec.hs b/test/Spec.hs
index 6cb9fac..a17f344 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -101,7 +101,7 @@ e2eTests = testGroup "e2e" [
let expectedLastAST = astInteger 12586269025
assertEqual "" expectedLastAST (last gotASTs),
- do let env = makeEnv [builtinAdd2, builtinEq2]
+ do let env = builtinEnv
let script1 = "(let a :thing)\
\(let b :thing)\
\(eq? a b)"
@@ -109,6 +109,14 @@ e2eTests = testGroup "e2e" [
runInlineScript "<test>" script1
let expectedLastAST = astBoolean True
+ assertEqual "" expectedLastAST (last gotASTs),
+
+ do let env = builtinEnv
+ script1 <- readFile "test/scripts/record1.milch"
+ (gotASTs, _) <- expectSuccessL env $
+ runInlineScript "<test>" script1
+
+ let expectedLastAST = astInteger 369
assertEqual "" expectedLastAST (last gotASTs)
]
diff --git a/test/scripts/record1.milch b/test/scripts/record1.milch
new file mode 100644
index 0000000..8594179
--- /dev/null
+++ b/test/scripts/record1.milch
@@ -0,0 +1,15 @@
+(import "core/common")
+
+(record A
+ foo
+ bar)
+
+(let a (A/create "123" 456))
+
+(pipe a [
+ A/get-foo
+ parse-int
+ (* 3)
+ (A/create (A/get-foo a))
+ (A/get-bar)
+])