Skip to content

Commit

Permalink
v1.1.0 :: Add modules and GitHub Actions. Rework examples and README.
Browse files Browse the repository at this point in the history
  • Loading branch information
ProggerX committed Mar 3, 2025
1 parent 3e8ad3e commit 625a1d3
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 86 deletions.
83 changes: 83 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Haskell CI

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

permissions:
contents: read

jobs:
build:

runs-on: windows-latest

steps:
- uses: actions/checkout@v4
- uses: haskell-actions/setup@v2
with:
ghc-version: '9.6.6'
cabal-version: '3.12'

- name: Cache
uses: actions/cache@v3
env:
cache-name: cache-cabal
with:
path: ~/.cabal
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Install dependencies
run: |
cabal update
cabal build --only-dependencies
- name: Build
run: cabal build
- name: Install
run: cabal install --installdir .
- uses: actions/upload-artifact@v4
with:
name: lapse-windows
path: |
lapse.exe
build-linux:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: haskell-actions/setup@v2
with:
ghc-version: '9.6.6'
cabal-version: '3.12'

- name: Cache
uses: actions/cache@v3
env:
cache-name: cache-cabal
with:
path: ~/.cabal
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Install dependencies
run: |
cabal update
cabal build --only-dependencies
- name: Build
run: cabal build --enable-executable-static
- name: Install
run: cabal install --installdir . --enable-executable-static
- uses: actions/upload-artifact@v4
with:
name: lapse-linux
path: |
lapse
31 changes: 11 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
# lapse-hs (WIP)
This will be a Lapse (my LISP dialect) interpreter in future.
This is Lapse (my Lisp dialect) interpreter in future.

Just an example:
```lapse
(set message "Running an example\n...")
(print message)
(print "Enter your name:")
(set name (getline))
(let ((message (concat "Hello, " name "!"))) (print message))
(set result (let ((a 1) (b 2) (c 3))
'(a b c ,a ,b ,c (+ a b c) ,(+ a b c))
))
(print result)
(defmacro if (c then else) (cond ((eval c) then) (1 else)))
(if (> 1 2) (print "1 > 2")
(print "1 <= 2"))
(if (< 1 2) (print "1 < 2")
(print "1 >= 2"))
(defn fac (x) (if (< x 1) 1 (* x (fac (- x 1)))))
(print (fac 6))
```
Examples: [here](https://github.com/ProggerX/lapse-hs/blob/master/example/example.lp)

## How to use this/try this
- Go to [this page](https://github.com/ProggerX/lapse-hs/actions)
- Click on latest successful action run (or run from specific tag)
- Find an "Artifacts" section and download version for your OS. (If you can't see download button, log in to your github account)
- Unzip this artifact and you will get an executable of Lapse interpreter
- To run some code, write this code in ".lp" file (in fact, extension does not matter) and run `<path-to-lapse-interpreter> <path-to-file>` command in command line
- (You can find example files [here](https://github.com/ProggerX/lapse-hs/blob/master/example/example.lp))
- Also, Lapse provides a clean REPL by `<path-to-lapse-interpreter>` command
19 changes: 13 additions & 6 deletions test-code.lp → example/example.lp
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
(import "std" "io")

(set message "Running an example\n...")
(print message)
(print "Enter your name:")

(write "Enter your name: ")
(flush)
(set name (getline))
(let ((message (concat "Hello, " name "!"))) (print message))
(set result (let ((a 1) (b 2) (c 3))
'(a b c ,a ,b ,c (+ a b c) ,(+ a b c))
))
(print result)

(defmacro if (c then else) (cond ((eval c) then) (1 else)))
(if (> 1 2) (print "1 > 2")
(print "1 <= 2"))
(if (< 1 2) (print "1 < 2")
(print "1 >= 2"))

(defn fac (x) (if (< x 1) 1 (* x (fac (- x 1)))))
(print (fac 6))
(set fact6 (show (fac 6)))
(print (concat "Factorial of 6: " fact6))

(import "fibonacci.lp")
(set fib8 (show (fib 8)))
(print (concat "8-th Fibonacci number: " fib8))
9 changes: 9 additions & 0 deletions example/fibonacci.lp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(import "std")

(defmacro if (c then else) (cond ((eval c) then) (1 else)))
(defn fib (a)
(if (< a 2)
a
(+ (fib (- a 1)) (fib (- a 2)))
)
)
2 changes: 2 additions & 0 deletions repl-example.lp → example/repl-example.lp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
(import "std" "io")

(defn repl ()
(eval
(write "(repl@lapse)>> ")
Expand Down
4 changes: 2 additions & 2 deletions lapse.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 3.0
name: lapse
version: 1.0.2
version: 1.1.0
license: GPL-3.0-only
license-file: LICENSE
author: ProggerX
Expand All @@ -19,8 +19,8 @@ library
, Lapse.Scopes
, Lapse.Types
, Lapse.Lambda
, Lapse.Prelude
, Lapse.Parser
, Lapse.Modules
hs-source-dirs:
src
build-depends:
Expand Down
2 changes: 1 addition & 1 deletion src/Lapse.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module Lapse where
import Control.Monad ((<=<))
import Control.Monad.State (evalStateT)
import Lapse.Eval (eval)
import Lapse.Modules (initIOState, initState)
import Lapse.Parser (parse)
import Lapse.Prelude (initIOState, initState)
import Lapse.Types (LapseM, Value (..))

list :: [Value m] -> Value m
Expand Down
108 changes: 108 additions & 0 deletions src/Lapse/Modules.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
module Lapse.Modules where

import Control.Exception (SomeException (..), catch)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.State (evalStateT, runStateT)
import Data.Map.Strict (Map, empty, fromList, (!?))
import Lapse.Eval (eval)
import Lapse.Lambda (define, defmacro, lambda, macro)
import Lapse.Operators
import Lapse.Parser (parse)
import Lapse.Scopes (addScope, addScopes)
import Lapse.Types (Func, LapseM, Scope, Scopes, Value (..))
import System.IO (
IOMode (ReadMode),
hClose,
openFile,
readFile',
)

std :: (Monad m) => Scope m
std =
fromList
[ ("+", Function ladd)
, ("*", Function lmul)
, ("/", Function ldiv)
, ("-", Function lsub)
, ("<", Function llss)
, (">", Function lgrt)
, ("==", Function leql)
, ("^", Function lpow)
, ("sqrt", Function lsqr)
, ("let", Macros llet)
, ("set", Macros lset)
, ("cond", Macros cond)
, ("map", Function lmap)
, ("double", Function ldouble)
, ("list", Function llist)
, ("gensym", Function gensym)
, ("eval", Function leval)
, ("nil", Nil)
, ("raw", Macros lraw)
, ("fst", Function lfst)
, ("snd", Function lsnd)
, ("fact", Function lfac)
, ("concat", Function lcon)
, ("show", Function lshow)
, ("lambda", Macros lambda)
, ("defn", Macros define)
, ("macro", Macros macro)
, ("defmacro", Macros defmacro)
, ("read", Function lread)
]

io :: Scope IO
io =
fromList
[ ("print", Function lprint)
, ("write", Function lwrite)
, ("getline", Function lgetl)
, ("flush", Function lflush)
]

builtins :: Map String (Scope IO)
builtins =
fromList
[ ("std", std)
, ("io", io)
]

fileExists :: FilePath -> IO Bool
fileExists path =
do
handle <- openFile path ReadMode
hClose handle
return True
`catch` (\(SomeException _) -> return False)

getScopesIO' :: LapseM IO a -> IO (Scopes IO)
getScopesIO' = (snd <$>) . (`evalStateT` 0) . (`runStateT` initIOState)

getScopesIO :: String -> IO (Scopes IO)
getScopesIO = getScopesIO' . mapM eval . parse

limport :: Func IO
limport (Pair (String s) Nil) = case builtins !? s of
Just x -> addScope x >> pure Nil
Nothing -> do
exists <- liftIO $ fileExists s
if exists
then do
fileText <- liftIO $ readFile' s
scopes <- liftIO $ getScopesIO fileText
addScopes scopes
pure Nil
else error $ "Can't find module: " ++ s
limport (Pair (String s) a) = limport (Pair (String s) Nil) >> limport a
limport _ = error "import argument must be string"

prelude :: Scope IO
prelude =
fromList
[("import", Macros limport)]

initState :: (Monad m) => Scopes m
initState = [empty, std]

initIOState :: Scopes IO
initIOState = [empty, prelude]
56 changes: 0 additions & 56 deletions src/Lapse/Prelude.hs

This file was deleted.

8 changes: 7 additions & 1 deletion src/Lapse/Scopes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ module Lapse.Scopes where
import Control.Monad.State (get, gets, put)
import Data.Map.Strict ((!?))
import Data.Map.Strict qualified as Map
import Lapse.Types (LapseM, Scopes, Value (..))
import Lapse.Types (LapseM, Scope, Scopes, Value (..))

newScope :: (Monad m) => LapseM m ()
newScope = get >>= put . (Map.empty :)

addScope :: (Monad m) => Scope m -> LapseM m ()
addScope = (get >>=) . (put .) . (:)

addScopes :: (Monad m) => Scopes m -> LapseM m ()
addScopes = (get >>=) . (put .) . (++) . foldr (:) []

dropScope :: (Monad m) => LapseM m ()
dropScope = get >>= put . tail

Expand Down

0 comments on commit 625a1d3

Please sign in to comment.