Skip to content

Commit 85c740e

Browse files
committed
chore: update to latest version with webclient
1 parent 7aacf1e commit 85c740e

37 files changed

+6524
-820
lines changed

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.PHONY: run build clean
2+
3+
run:
4+
stack run -- ${ARGS}
5+
6+
build:
7+
stack build
8+
9+
clean:
10+
stack clean
11+

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# l-lang

app/Main.hs

Lines changed: 220 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,228 @@
1+
{-# LANGUAGE LambdaCase #-}
2+
{-# LANGUAGE OverloadedStrings #-}
3+
14
module Main (main) where
25

3-
import Parser (interpretFile)
6+
import Control.Exception (SomeException, catch, evaluate)
7+
import Control.Monad (foldM)
8+
import Control.Monad.IO.Class (liftIO)
9+
import Data.Aeson (ToJSON (..), Value (..), object, (.=))
10+
import Data.Char (isSpace)
11+
import Data.IORef
12+
import Data.List (dropWhile)
13+
import qualified Data.Map as Map
14+
import Data.Text.Lazy (Text, pack, unpack)
15+
import Data.Text.Lazy.Encoding (decodeUtf8)
16+
import Evaluator
17+
import Network.Wai.Middleware.Cors (simpleCors)
18+
import Network.Wai.Middleware.RequestLogger (logStdoutDev)
19+
import Parser
420
import System.Environment (getArgs)
21+
import System.IO (getLine, hFlush, isEOF, stdout)
22+
import System.Timeout (timeout)
23+
import Value
24+
import Web.Scotty
25+
26+
-- ----------------------------------------------------------------------------
27+
-- Web Server Specific Definitions
28+
-- ----------------------------------------------------------------------------
29+
30+
data StepResult = StepResult {output :: String, ast :: Maybe String} deriving (Show)
31+
32+
instance ToJSON StepResult where
33+
toJSON (StepResult out maybeAst) = object ["output" .= out, "ast" .= maybeAst]
34+
35+
data MultiEvalResult = MultiEvalResult
36+
{ steps :: [StepResult],
37+
finalError :: Maybe String,
38+
finalEnvironment :: Env,
39+
traceLog :: Maybe [String] -- Field for combined trace log
40+
}
41+
deriving (Show)
42+
43+
instance ToJSON MultiEvalResult where
44+
toJSON (MultiEvalResult stepList err envMap mLog) =
45+
object
46+
[ "steps" .= stepList,
47+
"finalError" .= err,
48+
"finalEnvironment" .= envMap,
49+
"traceLog" .= mLog
50+
]
51+
52+
-- Timeout for evaluation in microseconds (e.g., 5 seconds)
53+
evaluationTimeoutDuration :: Int
54+
evaluationTimeoutDuration = 5 * 1000 * 1000 -- 5 seconds
55+
56+
-- Function to process a single line for the web handler's fold
57+
-- Takes the environment accumulated so far, and the current line string
58+
-- Returns Either Error Message (New Env, Output String, AST String, Trace Log for this line)
59+
processWebLine :: Env -> String -> IO (Either String (Env, String, Maybe String, TraceLog))
60+
processWebLine currentEnv line = do
61+
let trimmedLine = dropWhile isSpace line
62+
if null trimmedLine
63+
then return $ Right (currentEnv, "", Nothing, [])
64+
else case parse parseDefinition trimmedLine of
65+
Just ((name, defExpr), "") -> do
66+
let astString = Just (show defExpr)
67+
let envForDefEval = Map.insert name valueForDef currentEnv
68+
-- evalResultAndTrace now holds Either String (Value, TraceLog)
69+
evalResultAndTrace = eval envForDefEval defExpr
70+
valueForDef = case evalResultAndTrace of
71+
Left e -> error $ "Internal error: accessing value from failed recursive definition for " ++ name ++ ": " ++ e
72+
Right (v, _) -> v
73+
case evalResultAndTrace of
74+
Left err -> return $ Left $ "Error in definition '" ++ name ++ "': " ++ err
75+
Right (_, trace) -> return $ Right (envForDefEval, "Defined (rec): " ++ name, astString, trace)
76+
_ ->
77+
case parse parseExpr trimmedLine of
78+
Just (exprAST, "") -> do
79+
let astString = Just (show exprAST)
80+
case eval currentEnv exprAST of -- eval now returns Either String (Value, TraceLog)
81+
Left errMsg -> return $ Left errMsg
82+
Right (val, trace) -> return $ Right (currentEnv, show val, astString, trace)
83+
_ ->
84+
return $ Left $ "Parse Error on line: " ++ take 40 trimmedLine
85+
86+
-- Function to run the Scotty Web Server
87+
runWebServer :: IO ()
88+
runWebServer = do
89+
putStrLn "Starting L Language Web Server on port 3000 (Stateful, Multi-line, with Trace)..."
90+
sharedEnvRef <- newIORef initialEnv -- Initialize shared state with built-ins
91+
scotty 3000 $ do
92+
middleware simpleCors
93+
-- middleware logStdoutDev
94+
95+
get "/" $ do
96+
html $
97+
mconcat
98+
[ "<h1>L Web Interface</h1>",
99+
"<p>Enter code (definitions or expressions, one per line) below and click Evaluate.</p>",
100+
"<textarea id='code' rows='10' cols='80' style='font-family:monospace;'></textarea><br/>",
101+
"<button onclick='evaluateCode()'>Evaluate</button>",
102+
"<h2>Outputs / Steps:</h2><div id='outputSteps' style='background-color:#f0f0f0; padding:10px; border:1px solid #ccc; min-height:20px; white-space: pre-wrap;'></div>",
103+
"<h2>Evaluation Trace:</h2><pre id='outputTrace' style='background-color:#e0e0e0; padding:10px; border:1px solid #bbb; max-height: 300px; overflow-y: auto; white-space: pre-wrap;'></pre>",
104+
"<h2>Final Environment:</h2><pre id='outputEnv' style='background-color:#f0f0f0; padding:10px; border:1px solid #ccc; min-height:20px; max-height: 200px; overflow-y: auto;'></pre>",
105+
"<script src='/script.js'></script>"
106+
]
107+
108+
get "/script.js" $ do
109+
setHeader "Content-Type" "application/javascript"
110+
file "frontend.js"
111+
112+
post "/evaluate" $ do
113+
codeText <- body
114+
let codeString = unpack (decodeUtf8 codeText)
115+
let codeLines = lines codeString
116+
117+
initialEnvState <- liftIO $ readIORef sharedEnvRef
118+
119+
-- Accumulator: Either Error (CurrentEnvInBlock, List of StepResults, Accumulated TraceLog)
120+
finalResult <-
121+
foldM
122+
( \acc line -> case acc of
123+
Left err -> return $ Left err
124+
Right (env, stepsAcc, traceAcc) -> do
125+
lineResult <- liftIO $ processWebLine env line
126+
case lineResult of
127+
Left err -> return $ Left err
128+
Right (newEnv, outputStr, maybeAstStr, lineTrace) ->
129+
let step = StepResult outputStr maybeAstStr
130+
in return $ Right (newEnv, if null outputStr && maybeAstStr == Nothing then stepsAcc else stepsAcc ++ [step], traceAcc ++ lineTrace)
131+
)
132+
(Right (initialEnvState, [], []))
133+
codeLines
134+
135+
response <- case finalResult of
136+
Left errorMsg ->
137+
return $ MultiEvalResult [] (Just errorMsg) initialEnvState Nothing
138+
Right (finalEnvState, steps, accumulatedTrace) -> do
139+
liftIO $ atomicModifyIORef' sharedEnvRef $ \_ -> (finalEnvState, ())
140+
return $ MultiEvalResult steps Nothing finalEnvState (Just accumulatedTrace)
141+
142+
json response
143+
144+
-- ----------------------------------------------------------------------------
145+
-- REPL Specific Definitions
146+
-- ----------------------------------------------------------------------------
147+
repl :: Env -> IO ()
148+
repl currentEnv = do
149+
putStr "L-Repl> "
150+
hFlush stdout
151+
eof <- isEOF
152+
if eof
153+
then putStrLn "\nGoodbye!"
154+
else do
155+
line <- getLine
156+
case line of
157+
":quit" -> putStrLn "Goodbye!"
158+
":env" -> do
159+
print currentEnv
160+
repl currentEnv
161+
_ -> handleInput line currentEnv
162+
163+
handleInput :: String -> Env -> IO ()
164+
handleInput line currentEnv =
165+
let trimmedLine = dropWhile isSpace line
166+
in if null trimmedLine
167+
then repl currentEnv
168+
else case parse parseDefinition trimmedLine of
169+
Just ((name, expr), "") -> do
170+
putStrLn $ "Evaluating potentially recursive definition for '" ++ name ++ "'..."
171+
let newEnv = Map.insert name value currentEnv
172+
evalResultAndTrace = eval newEnv expr -- Returns (Value, TraceLog)
173+
value = case evalResultAndTrace of
174+
Left err -> Prelude.error $ "Internal error: accessing value from failed recursive eval for " ++ name ++ ": " ++ err
175+
Right (val, _) -> val
176+
case evalResultAndTrace of
177+
Left err -> do
178+
putStrLn $ "Error in definition '" ++ name ++ "': " ++ err
179+
repl currentEnv
180+
Right (_, trace) -> do
181+
-- Definition succeeded
182+
putStrLn $ "Defined (rec): " ++ name
183+
putStrLn "--- Evaluation Trace (Definition) ---"
184+
mapM_ (putStrLn . (" " ++)) trace
185+
putStrLn "-------------------------------------"
186+
repl newEnv
187+
_ ->
188+
case parse parseExpr trimmedLine of
189+
Just (exprAST, "") -> do
190+
case eval currentEnv exprAST of -- Returns (Value, TraceLog)
191+
Left err -> putStrLn $ "Error: " ++ err
192+
Right (val, trace) -> do
193+
-- Expression evaluation succeeded
194+
print val
195+
putStrLn "--- Evaluation Trace ---"
196+
mapM_ (putStrLn . (" " ++)) trace
197+
putStrLn "----------------------"
198+
repl currentEnv
199+
Just (_, rest) -> do
200+
putStrLn ("Parse Error: Unexpected input near: '" ++ take 20 rest ++ "...'")
201+
repl currentEnv
202+
Nothing -> do
203+
putStrLn "Parse Error: Invalid input."
204+
repl currentEnv
205+
206+
runRepl :: IO ()
207+
runRepl = do
208+
putStrLn "Starting L Language REPL..."
209+
putStrLn "Define: name = expression (recursion supported)"
210+
putStrLn "Evaluate: expression (built-ins: map, filter, length)"
211+
putStrLn "Commands: :quit, :env"
212+
repl initialEnv
5213

214+
-- ----------------------------------------------------------------------------
215+
-- Main Entry Point - Selects Mode
216+
-- ----------------------------------------------------------------------------
6217
main :: IO ()
7218
main = do
8219
args <- getArgs
9220
case args of
10-
[filename] -> do
11-
result <- interpretFile filename
12-
print result
13-
_ -> putStrLn "Usage: program <filename>"
221+
["-r"] -> runRepl
222+
["-w"] -> runWebServer
223+
_ -> do
224+
putStrLn "Usage: <program-name> [-r | -w]"
225+
putStrLn " -r: Run interactive REPL"
226+
putStrLn " -w: Run web server on port 3000 (Stateful, Multi-line, with Trace)"
227+
putStrLn "Defaulting to REPL mode."
228+
runRepl

frontend.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// In frontend.js
2+
function evaluateCode() {
3+
const codeInput = document.getElementById('code').value;
4+
const outputResultArea = document.getElementById('outputResult');
5+
const outputASTArea = document.getElementById('outputAST');
6+
const outputEnvArea = document.getElementById('outputEnv');
7+
8+
// Clear previous outputs and provide feedback
9+
outputResultArea.textContent = 'Evaluating...';
10+
outputASTArea.textContent = '';
11+
outputEnvArea.textContent = '';
12+
13+
fetch('/evaluate', {
14+
method: 'POST',
15+
headers: {
16+
'Content-Type': 'text/plain',
17+
},
18+
body: codeInput
19+
})
20+
.then(response => {
21+
if (!response.ok) {
22+
throw new Error(`HTTP error! status: ${response.status}`);
23+
}
24+
return response.json();
25+
})
26+
.then(data => {
27+
outputASTArea.textContent = data.parsedAST || 'N/A'; // Display AST
28+
if (data.evaluationError) {
29+
outputResultArea.textContent = `Error: ${data.evaluationError}`;
30+
} else {
31+
outputResultArea.textContent = data.evaluationResult || 'No result (e.g., only definition)';
32+
}
33+
// Display Environment (pretty-printed JSON)
34+
outputEnvArea.textContent = JSON.stringify(data.currentEnvironment, null, 2) || 'N/A';
35+
})
36+
.catch(error => {
37+
outputResultArea.textContent = `Workspace Error: ${error.message}`;
38+
console.error('Error:', error);
39+
});
40+
}

index.html

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>L Interpreter</title>
5+
<style>
6+
textarea { width: 90%; font-family: monospace; }
7+
pre { background-color: #eee; padding: 10px; border: 1px solid #ccc; min-height: 50px; }
8+
</style>
9+
<meta name="viewport" content="width=device-width, initial-scale=1">
10+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-4Q6Gf2aSP4eDXB8Miphtr37CMZZQ5oXLH2yaXMJ2w8e2ZtHTl7GptT4jmndRuHDT" crossorigin="anonymous">
11+
12+
13+
</head>
14+
<body>
15+
<h1>LambdaCalc Web Interface</h1>
16+
<textarea id="code" rows="10" cols="80" placeholder="Enter your L program here..."></textarea><br/>
17+
<button onclick="evaluateCode()">Evaluate</button>
18+
<h2>Result:</h2>
19+
<pre id="output"></pre>
20+
21+
<script src="frontend.js"></script> </body>
22+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-j1CDi7MgGQ12Z7Qab0qlWQ/Qqz24Gc6BM0thvEMVjHnfYGF0rmFCozFSxQBxwHKO" crossorigin="anonymous"></script>
23+
24+
</html>

0 commit comments

Comments
 (0)