Compare commits

16 Commits
master ... b-r

Author SHA1 Message Date
320817c8a1 hw2: tmp init 2026-03-05 23:06:34 -08:00
4aa6836dab lab08: impl 2026-03-05 23:06:34 -08:00
8a13b621bf lab08: init 2026-03-05 23:06:34 -08:00
278f9dce2e lab07: impl applicative 2026-03-05 23:06:34 -08:00
64c43ed46f lab07: impl functor 2026-03-05 23:06:34 -08:00
a2e3a80c61 lab07: init 2026-03-05 23:06:34 -08:00
ba60218766 lab06: impl 2026-03-05 23:06:34 -08:00
10d551b4bc lab06: init 2026-03-05 23:06:34 -08:00
16be5b2691 lab05: impl 2026-02-21 21:30:34 -08:00
4cda0266e1 hw2: init 2026-02-21 21:09:49 -08:00
10451c7cdd hw1: submission 2026-02-15 00:31:58 -08:00
fa77c90d1b hw1: impl multiply, power of 2026-02-15 00:31:58 -08:00
b9e10c3e37 hw1: impl add, subtract, eq 2026-02-15 00:31:58 -08:00
ca9f9baf8b hw1: init 2026-02-14 23:42:53 -08:00
e8f7818ec9 lab04b: impl 2026-02-14 23:42:49 -08:00
14dc6dee28 lab04b: WIP 2026-02-14 23:42:41 -08:00
70 changed files with 181 additions and 4826 deletions

View File

@@ -34,8 +34,6 @@ data Expression =
| Op Binop Expression Expression
| If Expression Expression Expression -- if e1 then e2 else e3
| While Expression Expression -- while (e1) e2
| BoolOp BoolBinop Expression Expression
| Not Expression
deriving (Show)
data Binop =
@@ -49,11 +47,6 @@ data Binop =
| Le -- <= :: Int -> Int -> Bool
deriving (Show)
data BoolBinop =
And
| Or
deriving (Show)
data Value =
IntVal Int
| BoolVal Bool
@@ -65,61 +58,17 @@ data Value =
-- Be sure to explicitly check for a divide by 0 and throw an error.
applyOp :: Binop -> Value -> Value -> Value
applyOp Plus (IntVal i) (IntVal j) = IntVal $ i + j
applyOp Minus (IntVal i) (IntVal j) = IntVal $ i - j
applyOp Times (IntVal i) (IntVal j) = IntVal $ i * j
applyOp Divide (IntVal i) (IntVal j) = IntVal $ i `div` j
applyOp Gt (IntVal i) (IntVal j) = BoolVal $ i > j
applyOp Ge (IntVal i) (IntVal j) = BoolVal $ i >= j
applyOp Lt (IntVal i) (IntVal j) = BoolVal $ i < j
applyOp Le (IntVal i) (IntVal j) = BoolVal $ i <= j
applyOp _ _ _ = error "Not implemented for non-integer values"
applyOp _ _ _ = error "TBD"
applyBoolOp :: BoolBinop -> Value -> Value -> Value
applyBoolOp And (BoolVal b1) (BoolVal b2) = BoolVal $ b1 && b2
applyBoolOp Or (BoolVal b1) (BoolVal b2) = BoolVal $ b1 || b2
applyBoolOp _ _ _ = error "Not implemented for non-boolean values"
applyNot :: Value -> Value
applyNot (BoolVal b) = BoolVal $ not b
applyNot _ = error "Not implemented for non-boolean values"
-- Implement this function according to the specified semantics
evaluate :: Expression -> Store -> (Value, Store)
evaluate (Val v) s = (v, s)
evaluate (Var x) s = case Map.lookup x s of
Just v -> (v, s)
Nothing -> error "Variable not found"
evaluate (Assign x e) s =
let (v,s') = evaluate e s
in (v, Map.insert x v s')
evaluate (Sequence e1 e2) s =
let (_,s1) = evaluate e1 s
(v2,s') = evaluate e2 s1
in (v2, s')
evaluate (Op o e1 e2) s =
let (v1,s1) = evaluate e1 s
(v2,s') = evaluate e2 s1
in (applyOp o v1 v2, s')
evaluate (If e1 e2 e3) s =
let (v1,s1) = evaluate e1 s
in case v1 of
BoolVal b -> if b then evaluate e2 s1 else evaluate e3 s1
_ -> error "Not implemented for non-boolean values"
evaluate (While e1 e2) s =
let (v1,s1) = evaluate e1 s
in case v1 of
BoolVal b -> if b then
let (_, s2) = evaluate e2 s1
in evaluate (While e1 e2) s2
else (BoolVal False, s1)
_ -> error "Not implemented for non-boolean values"
evaluate (BoolOp o e1 e2) s =
let (v1,s1) = evaluate e1 s
(v2,s') = evaluate e2 s1
in (applyBoolOp o v1 v2, s')
evaluate (Not e) s =
let (v,s') = evaluate e s
in (applyNot v, s')
evaluate _ _ = error "TBD"
-- Evaluates a program with an initially empty state
run :: Expression -> (Value, Store)

Binary file not shown.

View File

@@ -20,7 +20,7 @@
\author{
CS 252: Advanced Programming Languages \\
Yuri Tatishchev \\
Prof. Thomas H. Austin \\
San Jos\'{e} State University \\
}
\date{}
@@ -43,11 +43,9 @@ you will implement the semantics for a small imperative language, named WHILE.
\newcommand{\whilee}[2]{\mbox{\tt while}~(#1)~#2}
\newcommand{\true}{\mbox{\tt true}}
\newcommand{\false}{\mbox{\tt false}}
\newcommand{\note}[1]{\mbox{\tt not}~{#1}}
\begin{figure}[H]
\begin{figure}\label{fig:lang}
\caption{The WHILE language}
\label{fig:lang}
\[
\begin{array}{llr}
\mydefhead{e ::=\qquad\qquad\qquad\qquad}{Expressions}
@@ -58,16 +56,12 @@ you will implement the semantics for a small imperative language, named WHILE.
\mydefcase{e ~op~ e}{binary operations}
\mydefcase{\ife e e e}{conditional expressions}
\mydefcase{\whilee e e}{while expressions}
\mydefcase{e ~boolop~ e}{boolean binary operations}
\mydefcase{\note e}{negation}
\\
\mydefhead{v ::=\qquad\qquad\qquad\qquad}{Values}
\mydefcase{i}{integer values}
\mydefcase{b}{boolean values}
\\
op ::= & + ~|~ - ~|~ * ~|~ / ~|~ > ~|~ >= ~|~ < ~|~ <= & \mbox{\emph{Binary operators}} \\
\\
boolop ::= & and ~|~ or & \mbox{\emph{Boolean binary operators}} \\
\end{array}
\]
\end{figure}
@@ -82,24 +76,141 @@ Once we have mutable references, other language constructs become more useful,
such as sequencing operations ($e_1;e_2$).
%--------------
\section{Semantics}
\newcommand{\bstep}[4]{{#1},{#2} \Downarrow {#3},{#4}}
% Format for a big-step evaluation rule.
% #1 is the name of the rule.
% #2 are the premises. Leave blank if there are none.
% #3 is the conclusion.
\newcommand{\bsrule}[3]{
%---------
\section{Small-step semantics}
\newcommand{\ssrule}[3]{
\rel{#1} &
\frac{\strut\begin{array}{@{}c@{}} #2 \end{array}}
{\strut\begin{array}{@{}c@{}} #3 \end{array}}
\\~\\
}
%\newcommand{\sstep}[4]{\ctxt[{#1}],{#2} \rightarrow \ctxt[{#3}],{#4}}
%\newcommand{\sstepraw}[4]{{#1},{#2} \rightarrow {#3},{#4}}
\newcommand{\sstep}[4]{{#1},{#2} \rightarrow {#3},{#4}}
\newcommand{\ctxt}{C}
\begin{figure}[H]
\caption{Big-step semantics for WHILE}
\label{fig:bigstep}
The small-step semantics for WHILE are given in Figure~\ref{fig:smallstep}.
%For the sake of brevity, these rules use \emph{evaluation contexts} ($\ctxt$),
%which specify which \emph{redex} will be evaluated next.
%The evaluation rules then apply to the ``hole'' ($\bullet$) in this context.
%
Most of these rules are fairly straightforward, but there are a couple of points
to note with the $\rel{ss-while}$ rule.
First of all, this is the only rule that makes a more complex expression
when it has finished.
(This rule is much cleaner when specified with the big-step operational semantics.)
Secondly, note the final value of this expression once the while loop completes.
It will \emph{always} be {\false} when it completes.
We could have created a special value, such as {\tt null},
or we could have made the while loop a statement that returns no value.
Both choices, however, would complicate our language needlessly.
%--------------
\section{YOUR ASSIGNMENT}
\newcommand{\bstep}[4]{{#1},{#2} \Downarrow {#3},{#4}}
\noindent
{\bf Part 1:}
Rewrite the operational semantic rules for WHILE in \LaTeX\
to use big-step operational semantics instead.
Submit both your \LaTeX\ source and the generated PDF file.
Extend your semantics with features to handle boolean values.
{\bf Do not treat these a binary operators.}
Specifically, add support for:
\begin{compactitem}
\item {\tt and}
\item {\tt or}
\item {\tt not}
\end{compactitem}
The exact behavior of these new features is up to you,
but should seem reasonable to most programmers.
\bigskip
\noindent
{\bf Part 2:}
Once you have your semantics defined,
download {\tt WhileInterp.hs} and implement the {\tt evaluate} function,
as well as any additional functions you need.
Your implementation must be consistent with your operational semantics,
{\it including your extensions for {\tt and}, {\tt or}, and {\tt not}}.
Also, you may not change any type signatures provided in the file.
Finally, implement the interpreter to match your semantics.
\bigskip
\noindent
{\bf Zip all files together into {\tt hw2.zip} and submit to Canvas.}
%\begin{figure}[H]\label{fig:smallstep}
%\caption{Small-step semantics for WHILE}
%{\bf Runtime Syntax:}
%\[
%\begin{array}{rclcl}
% \ctxt & \in & {Context} \quad & ::= & \quad \ctxt; e
% ~|~ \ctxt ~op~ e
% ~|~ v ~op~ \ctxt
% ~|~ \assign{x}{\ctxt}
% ~|~ \ife{\ctxt}{e_1}{e_2}
% ~|~ \bullet \\
% \sigma & \in & {Store} \quad & = & \quad {variable} ~\rightarrow ~v \\
% \\
%\end{array}
%\]
%{\bf Evaluation Rules:~~~ \fbox{$\sstepraw{e}{\sigma}{e'}{\sigma'}$}} \\
%\[
%\begin{array}{cc}
%\begin{array}{r@{\qquad}l}
%\ssrule{ss-var}{
% x \in domain(\sigma) \qquad \sigma(x)=v
%}{
% \sstep{x}{\sigma}{v}{\sigma}
%}
%\ssrule{ss-assign}{
%}{
% \sstep{\assign{x}{v}}{\sigma}{v}{\sigma[x:=v]}
%}
%\ssrule{ss-op}{
% v = v_1 ~op~ v_2
%}{
% \sstep{v_1~op~v_2}{\sigma}{v}{\sigma}
%}
%\end{array}
% &
%\begin{array}{r@{\qquad}l}
%\ssrule{ss-seq}{
%}{
% \sstep{v;e}{\sigma}{e}{\sigma}
%}
%\ssrule{ss-iftrue}{
%}{
% \sstep{\ife{\true}{e_1}{e_2}}{\sigma}{e_1}{\sigma}
%}
%\ssrule{ss-iffalse}{
%}{
% \sstep{\ife{\false}{e_1}{e_2}}{\sigma}{e_2}{\sigma}
%}
%\ssrule{ss-while}{
%}{
% \sstep{\whilee{e_1}{e_2}}{\sigma}{\ife{e_1}{e_2;\whilee{e_1}{e_2}}{\false}}{\sigma}
%}
%\end{array}
%\end{array}
%\]
%\end{figure}
%
\begin{figure}[H]\label{fig:smallstep}
\caption{Small-step semantics for WHILE}
{\bf Runtime Syntax:}
\[
\begin{array}{rclcl}
@@ -107,76 +218,70 @@ such as sequencing operations ($e_1;e_2$).
\\
\end{array}
\]
{\bf Evaluation Rules:~~~ \fbox{$\bstep{e}{\sigma}{e'}{\sigma'}$}} \\
{\bf Evaluation Rules:~~~ \fbox{$\sstep{e}{\sigma}{e'}{\sigma'}$}} \\
\[
\begin{array}{r@{\qquad\qquad}c}
\bsrule{B-Value}{}{
\bstep{v}{\sigma}{v}{\sigma}
%\begin{array}{cc}
\begin{array}{r@{\qquad}l}
\ssrule{ss-seqctx}{
\sstep{e_1}{\sigma}{e_1'}{\sigma'}
}{
\sstep{e_1;e_2}{\sigma}{e_1';e_2}{\sigma'}
}
\bsrule{B-Var}{
\ssrule{ss-seq}{
}{
\sstep{v;e}{\sigma}{e}{\sigma}
}
\ssrule{ss-opctx1}{
\sstep{e_1}{\sigma}{e_1'}{\sigma'}
}{
\sstep{e_1~op~e_2}{\sigma}{e_1'~op~e_2}{\sigma'}
}
\ssrule{ss-opctx2}{
\sstep{e_2}{\sigma}{e_2'}{\sigma'}
}{
\sstep{v_1~op~e_2}{\sigma}{v_1~op~e_2'}{\sigma'}
}
\ssrule{ss-op}{
v = v_1 ~op~ v_2
}{
\sstep{v_1~op~v_2}{\sigma}{v}{\sigma}
}
\end{array}
\begin{array}{r@{\qquad}l}
\ssrule{ss-var}{
x \in domain(\sigma) \qquad \sigma(x)=v
}{
\bstep{x}{\sigma}{v}{\sigma}
\sstep{x}{\sigma}{v}{\sigma}
}
\bsrule{B-Assign}{
\bstep{e}{\sigma}{v}{\sigma'}
\ssrule{ss-assignctx}{
\sstep{e_1}{\sigma}{e_1'}{\sigma'}
}{
\bstep{\assign{x}{e}}{\sigma}{v}{\sigma'[x:=v]}
\sstep{\assign{x}{e}}{\sigma}{\assign{x}{e'}}{\sigma'}
}
\bsrule{B-Seq}{
\bstep{e_1}{\sigma}{v_1}{\sigma'} \\
\bstep{e_2}{\sigma'}{v_2}{\sigma''}
\ssrule{ss-assign}{
}{
\bstep{e_1;e_2}{\sigma}{v_2}{\sigma''}
\sstep{\assign{x}{v}}{\sigma}{v}{\sigma[x:=v]}
}
\bsrule{B-Op}{
\bstep{e_1}{\sigma}{v_1}{\sigma'} \\
\bstep{e_2}{\sigma'}{v_2}{\sigma''} \\
v = apply(op, v_1, v_2)
\ssrule{ss-iftrue}{
}{
\bstep{e_1~op~e_2}{\sigma}{v}{\sigma''}
\sstep{\ife{\true}{e_1}{e_2}}{\sigma}{e_1}{\sigma}
}
\bsrule{B-IfTrue}{
\bstep{e_1}{\sigma}{\true}{\sigma'} \\
\bstep{e_2}{\sigma'}{v}{\sigma''}
\ssrule{ss-iffalse}{
}{
\bstep{\ife{e_1}{e_2}{e_3}}{\sigma}{v}{\sigma''}
\sstep{\ife{\false}{e_1}{e_2}}{\sigma}{e_2}{\sigma}
}
\bsrule{B-IfFalse}{
\bstep{e_1}{\sigma}{\false}{\sigma'} \\
\bstep{e_3}{\sigma'}{v}{\sigma''}
\end{array}
\]
\[
\begin{array}{r@{\qquad}l}
\ssrule{ss-ifctx}{
\sstep{e_1}{\sigma}{e_1'}{\sigma'}
}{
\bstep{\ife{e_1}{e_2}{e_3}}{\sigma}{v}{\sigma''}
\sstep{\ife{e_1}{e_2}{e_3}}{\sigma}{\ife{e_1'}{e_2}{e_3}}{\sigma'}
}
\bsrule{B-WhileTrue}{
\bstep{e_1}{\sigma}{\true}{\sigma'} \\
\bstep{e_2}{\sigma'}{v_1}{\sigma''} \\
\bstep{\whilee{e_1}{e_2}}{\sigma''}{v}{\sigma'''}
\ssrule{ss-while}{
}{
\bstep{\whilee{e_1}{e_2}}{\sigma}{v}{\sigma'''}
}
\bsrule{B-WhileFalse}{
\bstep{e_1}{\sigma}{\false}{\sigma'}
}{
\bstep{\whilee{e_1}{e_2}}{\sigma}{\false}{\sigma'}
}
% \bsrule{B-While}{
% \bstep{e_1}{\sigma}{v_1}{\sigma'} \\
% \bstep{e_2}{\sigma'}{v_2}{\sigma''}
% }{
% \bstep{\whilee{e_1}{e_2}}{\sigma}{\ife{v_1}{e_2;\whilee{e_1}{e_2}}{\false}}{\sigma''}
% }
\bsrule{B-Not}{
\bstep{e}{\sigma}{b}{\sigma'}
}{
\bstep{\note{e}}{\sigma}{\neg b}{\sigma'}
}
\bsrule{B-BoolOp}{
\bstep{e_1}{\sigma}{b_1}{\sigma'} \\
\bstep{e_2}{\sigma'}{b_2}{\sigma''} \\
b = apply(boolop, b_1, b_2)
}{
\bstep{e_1~boolop~e_2}{\sigma}{b}{\sigma''}
\sstep{\whilee{e_1}{e_2}}{\sigma}{\ife{e_1}{e_2;\whilee{e_1}{e_2}}{\false}}{\sigma}
}
\end{array}
\]

View File

@@ -1,251 +0,0 @@
{-
Name: Yuri Tatishchev
Class: CS 252
Assigment: HW3
Date: 2026-03-27
Description: Parser/interpreter for the WHILE language
-}
module WhileInterp (
Expression(..),
Binop(..),
Value(..),
runFile,
showParsedExp,
run
) where
import Data.Map (Map)
import qualified Data.Map as Map
import Text.ParserCombinators.Parsec
import Control.Monad.Except
-- We represent variables as strings.
type Variable = String
--We also represent error messages as strings.
type ErrorMsg = String
-- The store is an associative map from variables to values.
-- (The store roughly corresponds with the heap in a language like Java).
type Store = Map Variable Value
data Expression =
Var Variable -- x
| Val Value -- v
| Assign Variable Expression -- x := e
| Sequence Expression Expression -- e1; e2
| Op Binop Expression Expression
| If Expression Expression Expression -- if e1 then e2 else e3 endif
| While Expression Expression -- while e1 do e2 endwhile
deriving (Show)
data Binop =
Plus -- + :: Int -> Int -> Int
| Minus -- - :: Int -> Int -> Int
| Times -- * :: Int -> Int -> Int
| Divide -- / :: Int -> Int -> Int
| Gt -- > :: Int -> Int -> Bool
| Ge -- >= :: Int -> Int -> Bool
| Lt -- < :: Int -> Int -> Bool
| Le -- <= :: Int -> Int -> Bool
deriving (Show)
data Value =
IntVal Int
| BoolVal Bool
deriving (Show)
fileP :: GenParser Char st Expression
fileP = do
prog <- exprP
eof
return prog
exprP = do
e <- exprP'
rest <- optionMaybe restSeqP
return (case rest of
Nothing -> e
Just e' -> Sequence e e')
-- Expressions are divided into terms and expressions for the sake of
-- parsing. Note that binary operators **DO NOT** follow the expected
-- presidence rules.
--
-- ***FOR 2pts EXTRA CREDIT (hard, no partial credit)***
-- Correct the precedence of the binary operators.
exprP' = do
spaces
t <- termP
spaces
rest <- optionMaybe restP
spaces
return (case rest of
Nothing -> t
Just (":=", t') -> (case t of
Var varName -> Assign varName t'
_ -> error "Expected var")
Just (op, t') -> Op (transOp op) t t')
restSeqP = do
char ';'
exprP
transOp s = case s of
"+" -> Plus
"-" -> Minus
"*" -> Times
"/" -> Divide
">=" -> Ge
">" -> Gt
"<=" -> Le
"<" -> Lt
o -> error $ "Unexpected operator " ++ o
-- Some string, followed by an expression
restP = do
ch <- string "+"
<|> string "-"
<|> string "*"
<|> string "/"
<|> try (string "<=")
<|> string "<"
<|> try (string ">=")
<|> string ">"
<|> string ":=" -- not really a binary operator, but it fits in nicely here.
<?> "binary operator"
e <- exprP'
return (ch, e)
-- All terms can be distinguished by looking at the first character
termP = valP
<|> ifP
<|> whileP
<|> parenP
<|> varP
<?> "value, variable, 'if', 'while', or '('"
valP = do
v <- boolP <|> numberP
return $ Val v
boolP = do
bStr <- string "true" <|> string "false" <|> string "skip"
return $ case bStr of
"true" -> BoolVal True
"false" -> BoolVal False
"skip" -> BoolVal False -- Treating the command 'skip' as a synonym for false, for ease of parsing
numberP = do
n <- many1 digit
return $ IntVal (read n)
varP = do
x <- many1 letter
return $ Var x
ifP = do
string "if"
e1 <- exprP
string "then"
e2 <- exprP
string "else"
e3 <- exprP
string "endif"
return $ If e1 e2 e3
whileP = do
string "while"
e1 <- exprP
string "do"
e2 <- exprP
string "endwhile"
return $ While e1 e2
-- An expression in parens, e.g. (9-5)*2
parenP = do
char '('
e <- exprP
char ')'
return $ e
-- This function will be useful for defining binary operations.
-- Unlike in the previous assignment, this function returns an "Either value".
-- The right side represents a successful computaton.
-- The left side is an error message indicating a problem with the program.
-- The first case is done for you.
applyOp :: Binop -> Value -> Value -> Either ErrorMsg Value
applyOp Plus (IntVal i) (IntVal j) = Right $ IntVal $ i + j
applyOp Minus (IntVal i) (IntVal j) = Right $ IntVal $ i - j
applyOp Times (IntVal i) (IntVal j) = Right $ IntVal $ i * j
applyOp Divide (IntVal i) (IntVal j) = Right $ IntVal $ i `div` j
applyOp Gt (IntVal i) (IntVal j) = Right $ BoolVal $ i > j
applyOp Ge (IntVal i) (IntVal j) = Right $ BoolVal $ i >= j
applyOp Lt (IntVal i) (IntVal j) = Right $ BoolVal $ i < j
applyOp Le (IntVal i) (IntVal j) = Right $ BoolVal $ i <= j
applyOp _ v1 v2 = Left $ "Non-integer value pair: '" ++ show v1 ++ "', '" ++ show v2 ++ "' used in binary operation"
-- As with the applyOp method, the semantics for this function
-- should return Either values. Left <error msg> indicates an error,
-- whereas Right <something> indicates a successful execution.
evaluate :: Expression -> Store -> Either ErrorMsg (Value, Store)
evaluate (Val v) s = Right (v, s)
evaluate (Var x) s = case Map.lookup x s of
Just v -> Right (v, s)
Nothing -> Left $ "Variable " ++ x ++ " not found"
evaluate (Assign x e) s = do
(v,s') <- evaluate e s
return (v, Map.insert x v s')
evaluate (Sequence e1 e2) s = do
(_,s1) <- evaluate e1 s
(v2,s') <- evaluate e2 s1
return (v2, s')
evaluate (Op o e1 e2) s = do
(v1,s1) <- evaluate e1 s
(v2,s') <- evaluate e2 s1
v <- applyOp o v1 v2
return (v, s')
evaluate (If e1 e2 e3) s = do
(v1, s1) <- evaluate e1 s
(v', s') <- case v1 of
BoolVal True -> evaluate e2 s1
BoolVal False -> evaluate e3 s1
_ -> Left $ "Non-boolean value '" ++ show v1 ++ "' used as conditional"
return (v', s')
evaluate (While e1 e2) s = do
(v1,s1) <- evaluate e1 s
case v1 of
BoolVal True -> do
(_,s2) <- evaluate e2 s1
(v', s') <- evaluate (While e1 e2) s2
return (v', s')
BoolVal False -> Right $ (BoolVal False, s)
_ -> Left $ "Non-boolean value '" ++ show v1 ++ "' used as while condition"
-- Evaluates a program with an initially empty state
run :: Expression -> Either ErrorMsg (Value, Store)
run prog = evaluate prog Map.empty
showParsedExp fileName = do
p <- parseFromFile fileP fileName
case p of
Left parseErr -> print parseErr
Right exp -> print exp
runFile fileName = do
p <- parseFromFile fileP fileName
case p of
Left parseErr -> print parseErr
Right exp ->
case (run exp) of
Left msg -> print msg
Right (v,s) -> print $ show s

View File

@@ -1,6 +0,0 @@
X := 0 - 3;
if X < 0 then
X := 0 - X
else
skip
endif

View File

@@ -1,7 +0,0 @@
x := 7 + 3;
if x then
true
else
false
endif

View File

@@ -1,4 +0,0 @@
X := 3 + 9 * 4;
Y := 5 * 2 + 7;
Z := 360 / 12 / 3

View File

@@ -1,11 +0,0 @@
N := 2;
F := 1;
while N > 0 do
X := N;
Z := F;
while X > 1 do
F := Z + F;
X := X - 1
endwhile;
N := N - 1
endwhile

View File

@@ -1,20 +0,0 @@
***Testing abs.imp
Sequence (Assign "X" (Op Minus (Val (IntVal 0)) (Val (IntVal 3)))) (If (Op Lt (Var "X") (Val (IntVal 0))) (Assign "X" (Op Minus (Val (IntVal 0)) (Var "X"))) (Val (BoolVal False)))
"fromList [(\"X\",IntVal 3)]"
***Testing fact.imp
Sequence (Assign "N" (Val (IntVal 2))) (Sequence (Assign "F" (Val (IntVal 1))) (While (Op Gt (Var "N") (Val (IntVal 0))) (Sequence (Assign "X" (Var "N")) (Sequence (Assign "Z" (Var "F")) (Sequence (While (Op Gt (Var "X") (Val (IntVal 1))) (Sequence (Assign "F" (Op Plus (Var "Z") (Var "F"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1)))))) (Assign "N" (Op Minus (Var "N") (Val (IntVal 1)))))))))
"fromList [(\"F\",IntVal 2),(\"N\",IntVal 0),(\"X\",IntVal 1),(\"Z\",IntVal 2)]"
***Testing times.imp
Sequence (Assign "X" (Val (IntVal 10))) (Sequence (Assign "Y" (Val (IntVal 3))) (Sequence (Assign "Z" (Val (IntVal 0))) (While (Op Gt (Var "X") (Val (IntVal 0))) (Sequence (Assign "Z" (Op Plus (Var "Z") (Var "Y"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1))))))))
"fromList [(\"X\",IntVal 0),(\"Y\",IntVal 3),(\"Z\",IntVal 30)]"
***Testing test.imp
Sequence (Assign "X" (Op Plus (Op Minus (Op Plus (Val (IntVal 1)) (Val (IntVal 2))) (Val (IntVal 3))) (Op Plus (Val (IntVal 1)) (Val (IntVal 3))))) (Sequence (Assign "Y" (Val (IntVal 0))) (While (Op Gt (Var "X") (Val (IntVal 0))) (Sequence (Assign "Y" (Op Plus (Var "Y") (Var "X"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1)))))))
"fromList [(\"X\",IntVal 0),(\"Y\",IntVal 10)]"
***Testing error.imp
Sequence (Assign "x" (Op Plus (Val (IntVal 7)) (Val (IntVal 3)))) (If (Var "x") (Val (BoolVal True)) (Val (BoolVal False)))
"Non-boolean value 'IntVal 10' used as conditional"

View File

@@ -1,19 +0,0 @@
***Testing abs.imp
Sequence (Assign "X" (Op Minus (Val (IntVal 0)) (Val (IntVal 3)))) (If (Op Lt (Var "X") (Val (IntVal 0))) (Assign "X" (Op Minus (Val (IntVal 0)) (Var "X"))) (Val (BoolVal False)))
"fromList [(\"X\",IntVal 3)]"
***Testing fact.imp
Sequence (Assign "N" (Val (IntVal 2))) (Sequence (Assign "F" (Val (IntVal 1))) (While (Op Gt (Var "N") (Val (IntVal 0))) (Sequence (Assign "X" (Var "N")) (Sequence (Assign "Z" (Var "F")) (Sequence (While (Op Gt (Var "X") (Val (IntVal 1))) (Sequence (Assign "F" (Op Plus (Var "Z") (Var "F"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1)))))) (Assign "N" (Op Minus (Var "N") (Val (IntVal 1)))))))))
"fromList [(\"F\",IntVal 2),(\"N\",IntVal 0),(\"X\",IntVal 1),(\"Z\",IntVal 2)]"
***Testing times.imp
Sequence (Assign "X" (Val (IntVal 10))) (Sequence (Assign "Y" (Val (IntVal 3))) (Sequence (Assign "Z" (Val (IntVal 0))) (While (Op Gt (Var "X") (Val (IntVal 0))) (Sequence (Assign "Z" (Op Plus (Var "Z") (Var "Y"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1))))))))
"fromList [(\"X\",IntVal 0),(\"Y\",IntVal 3),(\"Z\",IntVal 30)]"
***Testing test.imp
Sequence (Assign "X" (Op Plus (Op Minus (Op Plus (Val (IntVal 1)) (Val (IntVal 2))) (Val (IntVal 3))) (Op Plus (Val (IntVal 1)) (Val (IntVal 3))))) (Sequence (Assign "Y" (Val (IntVal 0))) (While (Op Gt (Var "X") (Val (IntVal 0))) (Sequence (Assign "Y" (Op Plus (Var "Y") (Var "X"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1)))))))
"fromList [(\"X\",IntVal 0),(\"Y\",IntVal 10)]"
***Testing error.imp
Sequence (Assign "x" (Op Plus (Val (IntVal 7)) (Val (IntVal 3)))) (If (Var "x") (Val (BoolVal True)) (Val (BoolVal False)))
test.hs: Non-boolean value '10' used as a conditional

View File

@@ -1,17 +0,0 @@
import WhileInterp
test f = do
putStrLn $ "***Testing " ++ f
showParsedExp f
runFile f
putStrLn ""
main :: IO ()
main = do
test "abs.imp"
--test "extra.imp"
test "fact.imp"
test "times.imp"
test "test.imp"
test "error.imp"

View File

@@ -1,6 +0,0 @@
X := ( ( 1 + 2 ) - 3 ) + ( 1 + 3 );
Y := 0;
while X>0 do
Y := Y+X;
X := X-1
endwhile

View File

@@ -1,7 +0,0 @@
X := 10;
Y := 3;
Z := 0;
while X > 0 do
Z := Z + Y;
X := X - 1
endwhile

View File

@@ -1,11 +0,0 @@
{
name: "Complex number list example",
nums: [
{ real: 42, imaginary: 1 },
{ real: 30, imaginary: 0 },
{ real: 15, imaginary: 7 }
],
knownIssues: null,
verified: false
}

View File

@@ -1,46 +0,0 @@
import Text.ParserCombinators.Parsec
import System.Environment
-- Each parser takes in a sequence of Chars and returns either
-- * an array of Strings ([String])
-- * an array of arrays of Strings ([String])
csvFile :: GenParser Char st [[String]]
-- A csv file contains 0 or more lines and is terminated by the end of the file
csvFile = do
result <- many line -- The result is an array of an array of Strings, i.e. [[String]]
eof -- match end of file
return result
line :: GenParser Char st [String]
-- A line is a comma separated list of values, ending with a newline character
line = do
result <- cells
char '\n' -- end of line, only works in Linux or OSX formatted files
return result
cells :: GenParser Char st [String]
cells = do
first <- cellContent
next <- remainingCells
return (first : next)
remainingCells :: GenParser Char st [String]
remainingCells = do
(char ',' >> cells) -- found a comma, so more cells
<|> (return []) -- No comma, so we wrap up an empty array
cellContent :: GenParser Char st String
cellContent =
many (noneOf ",\n") -- Matches any character that is not a comma or newline
parseCSV :: String -> Either ParseError [[String]]
parseCSV input = parse csvFile "(unknown)" input
main = do
args <- getArgs
p <- parseFromFile csvFile (head args)
case p of
Left err -> print err
Right csv -> print csv

View File

@@ -1,40 +0,0 @@
import Text.ParserCombinators.Parsec
import System.Environment
-- Each parser takes in a sequence of Chars and returns either
-- * an array of Strings ([String])
-- * an array of arrays of Strings ([String])
csvFile :: GenParser Char st [[String]]
csvFile = line `endBy` eol -- a series of lines ended by the end of the file
line :: GenParser Char st [String]
line = cell `sepBy` (char ',') -- a series of cells separated by commas
cell :: GenParser Char st String
cell = many (noneOf ",\n")
eol :: GenParser Char st String
{-
eol = char '\n' -- does not work on Windows
eol = string "\n" <|> string "\n\r" -- always matches first pattern
eol = string "\n\r" <|> string "\n" -- fails on non-windows, since the left hand tries and consumes the \n
eol = do -- works, but ugly, and changes the types
char '\n'
char '\r' <|> return '\n'
-}
eol = try (string "\n\r")
<|> string "\n"
<?> "end of line"
parseCSV :: String -> Either ParseError [[String]]
parseCSV input = parse csvFile "(unknown)" input
main = do
args <- getArgs
p <- parseFromFile csvFile (head args)
case p of
Left err -> print err
Right csv -> print csv

View File

@@ -1,116 +0,0 @@
import Text.ParserCombinators.Parsec
import System.Environment
import Data.List (intercalate)
data JValue = JString String
| JNumber Double
| JBool Bool
| JNull
| JObject [(String, JValue)]
| JArray [JValue]
deriving (Eq, Ord, Show)
jsonFile :: GenParser Char st JValue
jsonFile = do
result <- jsonArr <|> jsonObj
spaces
eof
return result
jsonElem :: GenParser Char st JValue
jsonElem = do
spaces
result <- jsonElem'
spaces
return result
jsonElem' = jsonArr
<|> jsonString
<|> jsonBool
<|> jsonNull
<|> jsonInt
<|> jsonObj
<?> "json element"
jsonString :: GenParser Char st JValue
jsonString = jsonStringDQ <|> jsonStringSQ
jsonStringDQ = do
char '"'
s <- many $ noneOf "\"" -- crude. does not allow double quotes within strings
char '"'
return $ JString s
jsonStringSQ = do
char '\''
s <- many $ noneOf "'" -- crude, same as above
char '\''
return $ JString s
jsonBool = do
bStr <- string "true" <|> string "false"
return $ case bStr of
"true" -> JBool True
"false" -> JBool False
jsonNull = do
string "null"
return JNull
jsonArr = do
char '['
arr <- jsonElem `sepBy` (char ',')
char ']'
return $ JArray arr
jsonInt = do
i <- many1 digit
return $ JNumber (read i :: Double)
jsonObj = do
char '{'
members <- jsonMember `sepBy` (char ',')
char '}'
return $ JObject members
jsonMember = do
spaces
key <- many $ noneOf ":"
char ':'
value <- jsonElem
return (key, value)
parseJSON :: String -> Either ParseError JValue
parseJSON input = parse jsonFile "(unknown)" input
prettyPrint :: JValue -> String
prettyPrint json = prettyPrint' json 0
prettyPrint' :: JValue -> Int -> String
prettyPrint' json indent = replicate indent ' ' ++ case json of
JString s -> show s
JNumber n -> show n
JBool b -> show b
JNull -> "null"
JArray arr -> "[\n"
++ intercalate ",\n" (map (\x -> prettyPrint' x (indent + 2)) arr)
++ "\n]"
JObject members -> "{\n"
++ intercalate ",\n" (map (
\(k, v) -> replicate (indent + 2) ' '
++ k
++ ": "
++ prettyPrint' v 0) members)
++ "\n"
++ replicate indent ' '
++ "}"
main = do
args <- getArgs
p <- parseFromFile jsonFile (head args)
case p of
Left err -> print err
Right json -> putStrLn (prettyPrint json)

View File

@@ -1,5 +0,0 @@
[
42,
33,
71
]

View File

@@ -1 +0,0 @@
['hi',"how",'are','u',null]

View File

@@ -1,4 +0,0 @@
Year,Make,Model,Length
1997,Ford,E350,2.34
2000,Mercury,Cougar,2.38
1 Year Make Model Length
2 1997 Ford E350 2.34
3 2000 Mercury Cougar 2.38

View File

@@ -1,317 +0,0 @@
PART 1 -- Introduction to Lambda Calculus
Lambda-calculus is the world's smallest programming language.
In its entirety, it consists of:
*functions (sometimes called abstractions)
*function application
*variables
Since the Greek letter lambda is not available in ASCII, a number of
programming languages use the backslash character instead. For instance,
in Haskell the identity function (lambda x.x) is written as follows:
> id = \x -> x
Despite the minimal nature of lambda-calculus, it is Turing-complete,
and we can encode a wide array of language features.
PART 2 -- Boolean values
To begin with, lets define boolean values.
We will name them 'tru' and 'fls' to avoid confusion with Haskell's
own constructs.
> tru = \x -> \y -> x
> fls = \x -> \y -> y
Defining booleans as functions may seem odd. In this case,
'tru' is really 'returnFirstParameter' and 'fls' is
'returnSecondParameter'. We can use these values to form an
'if then else' construct, which we will call 'test':
> test = \cond -> \thn -> \els -> cond thn els
The above function takes the boolean value and then applies it to the two
expressions. In the case that it is 'tru', it will result in
returning the first expression. If it is 'false', it will return
the second expression.
> testTru = transBool $ test tru tru fls
> testFls = transBool $ test fls tru fls
Note that Haskell cannot show lambdas, so the above test cases use a helper function;
everything to the right of the $ is lambda calculus.
Note that the transBool function can make use of the lambda calculus test function.
> transBool b = test b "true" "false"
With our boolean values, we can also define logical operators, such as 'and'.
(We will call it 'andd' to avoid conflict's with Haskell's 'and'.
> andd = \b -> \c -> b c fls
Why does this work? Here are a couple of tests to consider.
> testAnd1 = transBool $ andd tru tru
> testAnd2 = transBool $ andd fls tru
Define the 'nott' and 'orr' operators below without using andd.
> nott = \b -> b fls tru
> orr = \b -> \c -> b tru c
Here are some test cases:
> testOps1 = transBool $ orr fls (nott fls)
> testOps2 = transBool $ nott (orr fls tru)
PART 3 -- Pairs
We can also encode data structures in lambda calculus.
We'll look at pairs.
> pair = \f -> \s -> \b -> b f s
When this function is applied to two arguments,
'f' is bound to the first item in our pair and 's' to the second item.
The following is equivalent to (\b -> b tru fls):
> myPair = pair tru fls
In order for this to be useful, we must be able to get items out of the pair.
The following functions will get the first and second items, respectively:
> first = \p -> p tru
> second = \p -> p fls
The following lines produce "true" and "false" respectively.
Again, everything to the right of the $ is lambda calculus.
> testPairFst = transBool $ first myPair
> testPairSnd = transBool $ second myPair
From pairs, we can begin to build other data structures such as lists.
PART 4 -- Lists
Following the Lisp approach, we can define lists as pairs, where the first
part of the pair is the 'head' of the list, and the second element of the
list is the 'tail' -- the list containing all of the elements except for
the head.
In Lisp, the function that creates a list is called 'cons' -- we will use
that name here. Instead of 'head' and 'tail', we will use 'hd' and 'tl'
to avoid conflicts with the Haskell functions of the same name. As you can
see, we can easily define three of the basic list operations with minimal
effort:
> cons = pair
> hd = first
> tl = second
However, we need two more pieces; we need to have a way of representing
the empty list 'nil', and we need a way to test if a list is empty.
For 'isEmpty', we can exploit the design of pairs. As a reminder, pairs
are of the form (\b -> b f s). The 'b' is supposed to be a boolean,
which selects whether to return the first or second element. However,
we will instead provide it a lambda that ignores its arguments and
always returns false:
> isEmpty = \lst -> lst (\h -> \t -> fls)
With this definition of 'isEmpty', we now know what we need for 'nil'.
The empty list should always return true when it is passed in to 'isEmpty'.
Write the definition for 'nil' below. (If you are unsure of what the
definition should be, try walking through the derivation of
"isEmpty (pair 1 2)".
> nil = \x -> tru
Now we can create some sample lists. (Note that we are using Haskell numbers
just for easy of testing, though they are not part of the lambda calculus.)
> list1 = cons 0 (cons 1 (cons 2 nil))
> list2 = cons 3 (cons 4 (cons 5 nil))
The following test cases should print "0", "1", "false", and "true"
> testListHead = hd list1
> testListHeadTail = hd (tl list1)
> testNotEmpty = transBool $ isEmpty list1
> testEmpty = transBool $ isEmpty (tl (tl (tl list1)))
PART 5 -- Church numerals
With Boolean values, we were able to represent "primitive" values by
using special functions. However, there are only two possible values.
How can we represent a potentially infinite set of values, like numbers?
Church numerals define numbers as a collection of functions.
Below we will represent the numbers 0 through three:
> zero = \s -> \z -> z
> one = \s -> \z -> s z
> two = \s -> \z -> s (s z)
> three = \s -> \z -> s (s (s z))
So a number is represented by the number of 's' applications in the body.
Why is this a useful representation? And what is s exactly?
In these functions, z represents zero, and s represents a "successor".
As you might guess, we will need to create this successor function.
> scc = \n -> \s -> \z -> s (n s z)
So, instead of defining one, two, three manually, we could instead derive them
from zero.
> one' = scc zero
> two' = scc one'
> three' = scc two'
Walking through `scc zero` gives us the following steps:
(\n -> \s -> \z -> s (n s z)) zero
-> (\s -> \z -> s (zero s z))
= (\s -> \z -> s ((\s' -> \z' -> z') s z))
[expanding zero -- we use s' and z' to avoid confusion]
-> (\s -> \z -> s (\z' -> z') z)
-> (\s -> \z -> s z)
Therefore `scc one'` will have the following steps:
(\n -> \s -> \z -> s (n s z)) one'
-> (\s -> \z -> s (one' s z))
= (\s -> \z -> s ((\s' -> \z' -> s' z') s z))
-> (\s -> \z -> s ((\z' -> s z') z))
-> (\s -> \z -> s (s z))
So is this just a cute trick, or do the Church numerals actually represent numbers
in the real world in some meaningful way? Note that each number is essentially
instructions for how to construct itself given a successor function and zero.
Exploiting this fact, we can write a fairly simple function to translate
Church numerals into Haskell Ints with the right successor function and zero value.
> transChurchNums n = n (+1) 0
Using this principle, we can define addition of two numbers (m and n)
as getting the successor to n, m times.
> plus = \m -> \n -> \s -> \z -> m s (n s z)
> test3plus2 = transChurchNums $ plus three two
At a step by step level, here is what happens when the above code is invoked:
plus three two
= (\m -> \n -> \s -> \z -> m s (n s z)) three two
->* \s -> \z -> three s (two s z)
[In reality, the evaluation would stop here]
= \s -> \z -> three s ((\s' -> z' -> s' (s' z')) s z)
->* \s -> \z -> three s (s (s z))
= \s -> \z -> (\s' -> z' -> s' (s' (s' z'))) s (s (s z))
-> \s -> \z -> (\z' -> s (s (s z'))) (s (s z))
-> \s -> \z -> s (s (s (s (s z)))) [z' replaced with (s (s z))]
Alternately, we could define plus with our scc function:
> plus' = \m -> \n -> m scc n
Define multiplication in a similar manner:
> multiply = \m -> \n -> m (plus n) zero
> test3times2 = transChurchNums $ multiply three two
For ease of use, you may use the following function to convert a Haskell integer
to a Church numeral.
> church n = if n == 0
> then
> (\s -> \z -> z)
> else
> (\s -> \z -> s (church (n-1) s z))
PART 6 -- Recursion
Consider the following function, known as the divergent combinator.
omega = (\x -> x x) (\x -> x x)
Haskell will not accept the above combinator.
Evaluate this function by hand yourself.
After one step, what do you get?
function: (\x -> x x)
argument: (\x -> x x)
substitute x with (\x -> x x) in x x = (\x -> x x) (\x -> x x)
it evaluates to the same function, basically an infinite loop I think?
The omega function is not terribly useful, though it is interesting.
We can cause a lambda calculus program to go into an infinite loop.
A related function is the **fix combinator**, sometimes called the Y combinator.
(Like omega, fix cannot be run in Haskell).
fix = \f -> (\x -> f (\y -> x x y)) (\x -> f (\y -> x x y))
With the fix combinator, we can create recursive functions.
Let's consider the definition for factorial.
First, we need a way to test for zero.
> isZero = \n -> n (\x -> fls) tru
We also need a predecessor function, which proves to be fairly complex.
zz = pair zero zero
ss = \p -> pair (snd p) (plus one (snd p))
prd = \m -> fst (m ss zz)
With the fix combinator, we need to define a function that effectively does
one step of the recursive evaluation. Note the 'fct' parameter in the function
below.
g = \fct -> \n -> (test (isZero (prd n)) one (multiply n (fct (prd n))))
Using g and the fix function, we can define factorial.
factorial = fix g
To understand how this works, write out the evaluation steps for `factorial 3`.
-> factorial 3 = (fix g) 3
-> g (fix g) 3 = test (isZero (prd 3)) one (multiply 3 (g (prd 3)))
-> test (isZero (2)) one (multiply 3 (g (2)))
takes the false branch until g (1) then takes the true branch because isZero (prd 1) = true
> main :: IO ()
> main = do
> print $ testTru ++ " : expected true"
> print $ testFls ++ " : expected false"
> print $ testAnd1 ++ " : expected true"
> print $ testAnd2 ++ " : expected false"
> print $ testOps1 ++ " : expected true"
> print $ testOps2 ++ " : expected false"
> print $ testPairFst ++ " : expected true"
> print $ testPairSnd ++ " : expected false"
> print $ show testListHead ++ " : expected 0"
> print $ show testListHeadTail ++ " : expected 1"
> print $ testNotEmpty ++ " : expected false"
> print $ testEmpty ++ " : expected true"
> print $ show test3plus2 ++ " : expected 5"
> print $ show test3times2 ++ " : expected 6"

View File

@@ -1,23 +0,0 @@
var foldl = function (f, acc, array) {
}
console.log(foldl(function(x,y){return x+y}, 0, [1,2,3]));
var foldr = function (f, z, array) {
}
console.log(foldl(function(x,y){return x/y}, 1, [2,4,8]));
var map = function (f, array) {
}
console.log(map(function(x){return x+x}, [1,2,3,5,7,9,11,13]));
// Write a curry function as we discussed in class.
// Create a `double` method using the curry function
// and the following `mult` function.
function mult(x,y) {
return x * y;
}

View File

@@ -1,125 +0,0 @@
// Highlights some of the basic functionality of JavaScript.
// With JavaScript, we can specify new values with the var keyword.
// Using the keyword var is not strictly required, but it is considered a best practice.
var x=42, y=7;
// JavaScript is a functional language, in the sense that functions are first class objects.
// (WARNING: not everyone likes this definition of functional languages).
// We can write them in two different ways.
function add(a,b) {
return a + b;
}
var square = function(a) {
return a * a;
}
// Printing varies by platform. Node uses console.log.
console.log("x + y = " + add(x,y));
// That is annoying, so let's store it in another variable.
var print = console.log;
print("x^2 = " + square(x));
// Since functions are first class values, we can do interesting stuff with them.
function applyFunToX(f) {
f(x);
}
applyFunToX(print);
// JavaScript functions are **closures**. They remember their surrounding scope.
var getNextInt = function() {
var nextInt = 0;
return function() {
return nextInt++;
}
}();
print(getNextInt());
print(getNextInt());
print(getNextInt());
// Objects in JavaScript work a little differently.
var complex = { real: 3, imaginary: 1 };
print (complex);
var Dog = {
speak: function() { print('bark!'); }
};
rex = { name: 'rex', __proto__: Dog } // Manually setting the prototype chain -- not universal
rex.speak();
// We can add or remove properties at runtime
rex['favoriteToy'] = 'squeaky ball'; // could write this as rex.favoriteToy
print(rex);
//delete rex.name;
// We can override properties if we wish
rex.speak = function() { print('grr....'); }
rex.speak();
// Or delete them, with perhaps surprising results
delete rex.speak;
rex.speak();
// The more standard way of adding JavaScript objects: first create a constructor
function Cat(name, breed) {
this.name = name;
this.breed = breed;
this.speak = function() { print('meow!'); }
}
var garfield = new Cat('Garfield', 'Orange tabby');
print(garfield);
garfield.speak();
Cat.prototype.makeAngryNoise = function() {
print('hiss');
}
garfield.makeAngryNoise();
Cat.prototype.sayName = function() {
print("My name is " + this.name);
}
garfield.sayName();
garfield.favoriteFood = 'lasagna';
var animals = [garfield, new Cat('mimi', 'Calico'), rex];
var forEach = function(arr,f) {
var i;
for (i=0; i<arr.length; i++) {
f(arr[i]);
}
}
print("Displaying all animals");
forEach(animals, print);
print("Each animal say your name");
forEach(animals, function(animal) {
animal.speak();
//animal.sayName(); // Won't work -- rex has no sayName method
Cat.prototype.sayName.apply(animal);
});
Function.prototype.curry = function() {
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function () {
return that.apply(null, args.concat(slice.apply(arguments)));
};
};
var addOne = add.curry(1);
console.log(addOne(3));

View File

@@ -1,59 +0,0 @@
/*
Create a 'Student' constructor, like we did for Cat in class.
It should have the following fields:
*firstName
*lastName
*studentID
*display -- A function that prints out the firstName, lastName, and studentID number.
To invoke it, you should call `student.display()`.
Create an array of new students.
Add a 'graduated' property to just one of your students.
Now create another student **without** using the constructor.
(In other words, use the object literal `{}` syntax).
Set the prototype chain manually using the __proto__ field.
Make sure the display method still works (without you having to add it to the object explicitly).
*/
// Create a Student constructor
function Student(firstName, lastName, studentID) {
this.firstName = firstName;
this.lastName = lastName;
this.studentID = studentID;
}
// Add the display method to the prototype
// Manual student doesn't work if display is defined in the constructor
Student.prototype.display = function () {
console.log(`${this.firstName} ${this.lastName}, ID: ${this.studentID}`);
};
var students = [
new Student("John", "Smith", "12345"),
new Student("Jane", "Doe", "67890"),
new Student("Alice", "Johnson", "11111"),
];
// Add a 'graduated' property to just one of the students
students[0].graduated = true;
// Student without using the constructor (object literal syntax)
var manualStudent = {
firstName: "Bob",
lastName: "Wilson",
studentID: "99999",
__proto__: Student.prototype,
};
// Test
console.log("Students created with constructor:");
students.forEach(function (student) {
student.display();
if (student.graduated) {
console.log("This student has graduated!");
}
});
console.log("\nManual student with prototype:");
manualStudent.display();

View File

@@ -1,79 +0,0 @@
const net = require('net');
const eol = require('os').EOL;
const crypto = require('crypto');
const HASH_ALG = 'sha256';
const NAME_LEN = 10;
var srvr = net.createServer();
var clientList = [];
srvr.on('connection', function (client) {
client.name = calcName(client.remoteAddress, client.remotePort);
client.write('Welcome, ' + client.name + eol);
clientList.push(client);
client.on("data", function (data) {
if (data.at(0) !== "\\".charCodeAt(0)) {
broadcast(data, client);
return;
}
command = data
.subarray(1)
.toString()
.split(/[\s\r\n]+/)
.filter((s) => s.length > 0);
console.log(command);
switch (command[0]) {
case "list":
const list = clientList.map((c) => c.name).join(eol);
client.write("Connected Users" + eol + list + eol);
break;
case "rename":
if (command.length !== 2) {
client.write("Usage: \\rename <newname>" + eol);
break;
}
const oldName = client.name;
client.name = command[1];
client.write("You are now known as " + client.name + eol);
broadcast(oldName + " is now known as " + client.name + eol, client);
break;
case "private":
if (command.length < 3) {
client.write("Usage: \\private <name> <msg>" + eol);
break;
}
const targetName = command[1];
const msg = command.slice(2).join(" ");
const targetClient = clientList.find((c) => c.name === targetName);
if (targetClient) {
targetClient.write("(Private) " + client.name + " says " + msg + eol);
client.write("(Private) To " + targetClient.name + ": " + msg + eol);
} else {
client.write("User " + targetName + " not found." + eol);
}
break;
default:
client.write("Unknown command: " + command[0] + eol);
}
});
});
function calcName(remoteAddress, remotePort) {
let data = remoteAddress + ":" + remotePort;
let h = crypto.createHash(HASH_ALG).update(data).digest('hex')
return h.substring(0, NAME_LEN);
}
function broadcast(data, client) {
for (var i in clientList) {
if (client !== clientList[i]) {
clientList[i].write(client.name + " says " + data);
}
}
}
srvr.listen(9000);

View File

@@ -1,25 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAifK0oKcMmyyCBSSramC4RtcxGNkXUPcs3towqvEAhPisZeXaskG5JnvK
Tk1tdIsDcG9ioUOO3SKo1UwSC1tWf0XZiSxRWDh4fM7m+SoMBHcyE2rAo/EtOtv+hsa9Jsjg
q28eD9BJZyZJkFrz+pY6hrIQNeLJp4Iq1BlLwzni6Mhea7rBoU0pUG3rrvXMPoG/lPcOC5yi
bjL2fKAojG66ML5nfFDp3Cda/ccs8K6e/89cSwgYQBgz1qntd7djSW8moURrPsDdwhFo6/Dm
Ctds4Ct/sCilkGyz6NydK3n1wKk+JiRQlvmCZcOpiy9ywsq+t336Ly8JIIQ8fBdOKvSjAwID
AQABAoIBAB3Fg7YoV1AmCumRQG108lBXoQD72UxgDA8Mh/RcjwAjqxFSyiAujwb/MGo1y6mb
VuKtJ1wmeG7BTC68fCh3TfOp34j7R5MCnP5SqQadTHqqPuRx5aF8K4bIc+YifjAgqlxVXueK
zEy2tqmw93bddUQcqeL3Tw4XyaZ8zx0TISWbx6S8cI8VKYOz5l+8itNM8lH79i0IxKfAMeUR
QF0jfRxzWLCuOedY1/19/Q9rY9BB6pgi+7bh6hgMUbPRzWegMpuMuGHjk+23Ah6QKfSO4bd4
9Wq8CqYVLCqRpTUiSfHChXopnm3jGp6ELGYOOqxrI38M3X4izW2kv0mnk/o7UAECgYEAvw8L
laZs1j8LrisSiEp+453qlGB+Pq3h4OJK6gnKZDiD0K8K4sXl5a6wNTOlx77gdL6Xmmevqp8J
I787+TG/K4nrBnRObPPo/Jr6Ug/ON3oCRzhm5Ul5WMcJmkxEIgdJOzTxmuPdlMco9hU1rN3R
OzMgce9smZ0RUdEWN66naaMCgYEAuNY9RBjj7v95zfTGsbYv7xj2UYq0FBqU9s+PpMQFKfPg
bsPhoxOZgaWR2olVhj8XimhIv2eClOkISSORZryCVFzdFlYalrfzbj4ZLMzUrcEwhEY1x8Sj
GRwSb6LW23hH2CR47OBCAB/I1pskClXPhjqDPeoI4w2LlmzoOch6NyECgYBC65coLKnUhTKn
J6kPGeDGAysGG6I8fBUw+rI4tYMxhWYOMzGB6sUypPRNPjDj/OSLn7JkP8sU3S2LWtE74IPQ
UTeEBDrwCKLjz7i8A5M8dTzVC1lnGEwrn1OquunmbVzmIw45LXUQRo3/W8B4Mx21OZw0rCMC
zzQgzzaT/FXhNwKBgQCKl3lQuikOZ1j5aPQjBCxk5x6qfYoRGO1hMrjOHnyQvGAoLY/eKzAK
2g7HAfqyslQAX7lMa1yqMyEXVigMFITUpNzRhrZhFITewIk34k4GCrlKNSrWI0b36OiZ9EuO
PF4CXzacxno2hZ6d663WYgk86FrH7dfEB8AGhpqJ2RxqgQKBgD+NoVR7ER6o40pmCiBIT6O6
A8C3gSUZcNpNFRzB6EOZHPtRvmJ9xSmw5V/nRqIL744f+UYgTggbSmW1IJLnbbUX+hKkrscV
ldcil/J5xr56lm3eHpNiQc/h0mIWMSJwYl2YfMYmK6alASQzyfi1ibyw6PXMCYXm2MPKadI3
FjHP
-----END RSA PRIVATE KEY-----

View File

@@ -1,7 +0,0 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAifK0oKcMmyyCBSSramC4RtcxGNkXUPcs3towqvEAhPisZeXaskG5JnvKTk1t
dIsDcG9ioUOO3SKo1UwSC1tWf0XZiSxRWDh4fM7m+SoMBHcyE2rAo/EtOtv+hsa9Jsjgq28e
D9BJZyZJkFrz+pY6hrIQNeLJp4Iq1BlLwzni6Mhea7rBoU0pUG3rrvXMPoG/lPcOC5yibjL2
fKAojG66ML5nfFDp3Cda/ccs8K6e/89cSwgYQBgz1qntd7djSW8moURrPsDdwhFo6/DmCtds
4Ct/sCilkGyz6NydK3n1wKk+JiRQlvmCZcOpiy9ywsq+t336Ly8JIIQ8fBdOKvSjAwIDAQAB
-----END RSA PUBLIC KEY-----

View File

@@ -1,25 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA4g5WrWWU+FRbrBbT0J8vADRic+kYkggBFARM3VxWIKOA3o6/6oUi4PIE
dAocsLmYegL8c/xp/OTP/qpOZdFWOZ4/QgO/9ru9hbCIEUDQ1E3FwDCP835OHAZ7yv75gRD2
zcxsX95FZnN9Z3InwW3tNr59k97921OIV+Zjc55+HDHjovN+dM29jP72o1zBIRYsJxBSt1Op
hwkuDGA/IFwDrHqbidrFDR8cgGw6S0OV8TqhOaLmvCwXskJfp3/9uxo0JOO/rxKBU0WaqgU/
p816qtwVqdHmYkI04WxbihVvMdFFEMdv9l6igJlC6vbVO+qaCVBa17939nyuhexmJQP8IwID
AQABAoIBAELje0JTjIDudTG2j3Zro7yrw3YIwvxy4d7KZvQZHmMJ/lfcZW0/d9KXyjmLxHGG
eVehCpJHXhU2n1T7j7cQ+jKSYKVn9qVLaAjm81wnkaNAYAM+W1nxRauFP5wRVkKagmzi0C6N
1ns6MSEq5hiWLlrEguP+V59OWEmqIkNT4nz0t8iuF7HPaIHIx9X/+tDCRjJT9+y3L7qKkXjm
CdZf9x8MZCj9PffwjGwLhGUW5PTm140p4g3+KgVkQxDyaJnXmsMJxSrs9x4QTfJtrdtNt+Yv
jKF+e0kwEiKSKbGBGl/KeaFSA3O8lxMnSswim3/3R09Hwd5DTIaJPafYXjmBsgECgYEA8jRh
qmhzX09OcKkQOWcUUc6CtV/FL57hlP4t5cWR83ABAWmiagw4JStsnfw75VKo9zl9mlicXyYB
w4AVJo0eDKaxYn7YF8N441vQtpuvrGayulQg9CdggRfOWulIQH6g48JcpS06TeLAcTMDd6Py
4I9CzpAb3H/EJty7DNhdl9cCgYEA7u599/UGYIx1QSLADq/oNA3boY6ybkRSh0RhoF9OiDkM
kdE0di7Bu9AJHBboS+wkgnMH8vA7Vn6LsWslpStylqp+S5hU/845IAj7gHrazm0KRSSKi+XS
Ijipqz8TJqloptqDpzmC0JtVd3tdTD1RL6yMDePf38UB+sMX7BLGxJUCgYEAmNISOZpUCXQZ
sx4JPZ7bhE5/zH8UBNsDzKWgQhUp5Obry39iWfVBy7ode4V2KVjBgdQFe0ijyalKfZcoQT3T
EvpufA46vKu6cAIzq7SJ4IagCM8iR8s8qddifZwp2X/MdkjhedsSqagD8qjUbD3G9oeOCJWG
7tay5i5p2039p9sCgYEAkvEnlYQijOQ1BH8LoLy8UK8pIN2yjLAGFVyPseKLWKnFOjRwUDKH
QSs7PwQzjuXGuldmTxlBVPt0jAN6Xm8vBMwL6g/gToYUicgcbLKhuIxE4asMbedEaXlxl3iu
HXvYea0vhHuRIRw23smG4mlfcbNrWuuG9oahpgevwab2iVUCgYEAj0F7DiCUg2sJsLf1aZfi
5bjPCqA5oszwyuzB9j6SYDJ15Jprc744u7MdOTcnTVM/5k2hOgjTSWJYm9ghm7V9NAWFpyaP
2k4IR6686XF3us4Oh+LYNd1YqjgB+XW/cjS6bWtyk/nFJYAw9Ois2mTyuc0wba0xrP/n3oxT
QyzNeNs=
-----END RSA PRIVATE KEY-----

View File

@@ -1,7 +0,0 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA4g5WrWWU+FRbrBbT0J8vADRic+kYkggBFARM3VxWIKOA3o6/6oUi4PIEdAoc
sLmYegL8c/xp/OTP/qpOZdFWOZ4/QgO/9ru9hbCIEUDQ1E3FwDCP835OHAZ7yv75gRD2zcxs
X95FZnN9Z3InwW3tNr59k97921OIV+Zjc55+HDHjovN+dM29jP72o1zBIRYsJxBSt1Ophwku
DGA/IFwDrHqbidrFDR8cgGw6S0OV8TqhOaLmvCwXskJfp3/9uxo0JOO/rxKBU0WaqgU/p816
qtwVqdHmYkI04WxbihVvMdFFEMdv9l6igJlC6vbVO+qaCVBa17939nyuhexmJQP8IwIDAQAB
-----END RSA PUBLIC KEY-----

View File

@@ -1,25 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAh3tLOaiZvejIsA08OBMqYB4zdn5emWjcJpIHObW8H5jrcj9hpGe1IZ+Q
BpZ53318aD0uoJPSnOLXcLQR5v8AVdddmwaJr3meaW8PL/J5BPMpDyknxk2GgvZRxXy/eQkv
VLXbEIRzG5+8Zj8PXkc6rV+qlJEkK/0kuN2y9wz/vYN5z5Fq5qmGnZO665HBh15OxM9b30Fl
iBA/DXmD+zK8oR3Gwk3W8GISWmckAqFegZQr9ocY73JHFGTXYmxVbGiWvpmEIuES57bKQX4f
g3Xa2yQnW8jco48YX6ScPSpVWRiP+56YgAsoojHYr/Evn+fVE4OLqlLrNgWp9UWE6OKqVwID
AQABAoIBAHdfGxjihRPfV3RHNRfzSxMFiTKOhauFyPPzFoqypd7xTgKL9ex1tBK8FJq5Wwif
+E2gGsVBegDhnhGfl2aAqDr6V2+QgyApeHpNcIQ+kILb/QFusaz4AdzCS6pGEC8E+L8avLoA
+asNQ2KTjWb0O68m65WbjNN1aDW6oUUsHulK8wKC9M2pDs+nTd18IAkFVOop31rJ09bZcsnr
qiofRxjLhilvmH0F4nmUTfjrvNxNR0dWpeMEOnoIOg3OtTDqGcJFUI7ELwhYFt3vRP1sRXMS
mqgBmLmHK1p8B02YlVjXfmxAMR1ONk+G4uhLLdKkA+9DTNuc8PfEm7Ox+TXV9gECgYEA159D
kR/FnZhf64gylHKJei554oGB/EapC+LIViwHptSJ38Vk3vkWKomKPKmi3Se+S4e0FkEbgSqv
Tje6IzZwYFxEoZE+UNJSLXYTtnus4yhCItc0FHbCz+4bKuLf7Kij0b1L22LVJ6mObtG1ij1F
nJj381pkopQ579RT/14yT+cCgYEAoNop/ygCEp6tDAPr63HfaKDWY7Xy2B85nh62DiWqZ+eX
hkh/+mSKryRkkQiVSOxEzR5S2G68yXeiuGHF1N0XuwveDvSRP0wNJ5XVXY3F+cI9Y/Kdt2Kj
Hqf7Nm9bxlDHxj4LrL1PvJniG7Pk3e3eV+NIS8eNeWngEdZCrwDeRBECgYBYQM+e7qorvtpD
xRMuv1KVt8AbfXZ+k01NmBz3aD4ZgpSMCncm7DyuXG1ANw56BnJU585F7ZAcZiQql5SQJRAJ
4LoWhyRCYFNSWCnpXuV8dFXdfMfMOG+0O3jTLNkKPFwQ1LHfsawc98FcimkLk9iSJ+oUqMLX
6GNuTjGzLBLbsQKBgEwglKm/HKQFVy8NmBV5clvJa04LqeBhftp3/H/bkbfVYuBZ5b/y37Qe
jD4cCtNVhwZxKJJobo8HQQr0szWYYgfTiQgY9bk4XC10+2Nomt8m7/kitUQ6uarjo8BSmePV
DYeki8/4VQmU5u9nh5UFZCrCVdAPpMRmuHaYqjy7M3aRAoGAVKyUCZnu9AIkmCmuPEvF/7mQ
88zJ3y8BF44iTjKLquMb4SgK0Hur9EiPcufuzEHMiiHE/m6DPhE8OeV984ZlgwrXXcLvdnDL
N3lVkTGSNQjRmjK66ITqyTAscc/Hi/W/LzLl0ZjlgynJjtrJQRCIv5tEAaCWeEEY9zS1rD4j
4Jo=
-----END RSA PRIVATE KEY-----

View File

@@ -1,7 +0,0 @@
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAh3tLOaiZvejIsA08OBMqYB4zdn5emWjcJpIHObW8H5jrcj9hpGe1IZ+QBpZ5
3318aD0uoJPSnOLXcLQR5v8AVdddmwaJr3meaW8PL/J5BPMpDyknxk2GgvZRxXy/eQkvVLXb
EIRzG5+8Zj8PXkc6rV+qlJEkK/0kuN2y9wz/vYN5z5Fq5qmGnZO665HBh15OxM9b30FliBA/
DXmD+zK8oR3Gwk3W8GISWmckAqFegZQr9ocY73JHFGTXYmxVbGiWvpmEIuES57bKQX4fg3Xa
2yQnW8jco48YX6ScPSpVWRiP+56YgAsoojHYr/Evn+fVE4OLqlLrNgWp9UWE6OKqVwIDAQAB
-----END RSA PUBLIC KEY-----

View File

@@ -1,51 +0,0 @@
var crypto = require('crypto');
var fs = require('fs');
var events = require('events');
function Signer(privKeyFile) {
// Reads in and stores the private key
this.privateKey = fs.readFileSync(privKeyFile).toString('ascii');
}
Signer.prototype.signMessage = function(messageFileName) {
// Read in the file
var msg = fs.readFileSync(messageFileName).toString('ascii');
// Specify which signing algorithm we want to use
var sign = crypto.createSign('RSA-SHA256');
// Sign the message and return the signature in hexadecimal form
return sign.update(msg).sign(this.privateKey,'hex');
}
function Verifier(pubKeyFile) {
// Reads in and stores the public key
this.publicKey = fs.readFileSync(pubKeyFile).toString('ascii');
}
Verifier.prototype = new events.EventEmitter();
Verifier.prototype.verifySignature = function(messageFileName, signature) {
// Read in the file
var msg = fs.readFileSync(messageFileName).toString('ascii');
// Test the signature
var legit = crypto.createVerify('RSA-SHA256').update(msg)
.verify(this.publicKey, signature, 'hex');
if (legit) {
console.log("Signature is valid");
} else {
console.log("Invalid signature");
}
}
var verifier = new Verifier('./keys/charliePub.txt');
Verifier.prototype.on('signing', verifier.verifySignature);
// Valid signing
var signer = new Signer('./keys/charliePriv.txt');
var signature = signer.signMessage('./message.txt');
verifier.emit('signing', './message.txt', signature);
// Wrong signature used
signer = new Signer('./keys/bobPriv.txt');
signature = signer.signMessage('./message.txt');
verifier.emit('signing', './message.txt', signature);

View File

@@ -1,23 +0,0 @@
var net = require('net');
var eol = require('os').EOL;
var srvr = net.createServer();
srvr.on('connection', function(client) {
client.write('Hello there!' + eol);
client.end();
});
srvr.listen(9000);
// node tcpserver.js
/*
$ telnet 127.0.0.1 9000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello there!
Connection closed by foreign host.
*/

View File

@@ -1,11 +0,0 @@
/*global console*/
/*jslint this*/
var name = "Monty";
var r = new Rabbit("Python");
function Rabbit(name) {
this.name = name;
}
console.log(r.name); // ERROR!!!
console.log(name); // Prints "Python"

View File

@@ -1,15 +0,0 @@
// rabbit.ts
class Rabbit {
name: string;
constructor(name: string) {
this.name = name;
}
}
let myName: string = "Monty";
let r: Rabbit = new Rabbit("Python");
console.log(r.name); // ERROR!!!
console.log(myName); // Prints "Python"

View File

@@ -1,20 +0,0 @@
function Employee(fname, lname, salary) {
this.fname = fname;
this.lname = lname;
this.salary = salary;
}
var emps = [new Employee("Alice", "Alleyson", 95000),
new Employee("Robert", "Tables", "80000"),
new Employee("Charles", "Chaplin", 42350)];
function totalSalary(empList) {
var ttl = 0;
for (i in empList) {
ttl += empList[i].salary;
}
return ttl;
}
console.log(totalSalary(emps));

View File

@@ -1,28 +0,0 @@
/*global console*/
/*jslint for */
var largest = sortAndGetLargest([99, 2, 43, 8, 0, 21, 12]);
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function sortAndGetLargest(arr) {
var tmp = arr[0]; // largest elem
var i;
var j;
for (i = 0; i < arr.length; i += 1) {
if (arr[i] > tmp) {
tmp = arr[i];
}
for (j = i + 1; j < arr.length; j += 1) {
if (arr[i] < arr[j]) {
swap(arr, i, j);
}
}
}
return tmp;
}
console.log(largest); // should be 99, but prints 0

View File

@@ -1,28 +0,0 @@
// sortAndGetLargest.ts
function swap(arr: number[], i: number, j: number): void {
const tmp: number = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
function sortAndGetLargest(arr: number[]): number {
if (arr.length === 0) {
throw new Error("Array must not be empty");
}
// Sort the array in descending order
for (let i = 0; i < arr.length; i += 1) {
for (let j = i + 1; j < arr.length; j += 1) {
if (arr[i] < arr[j]) {
swap(arr, i, j);
}
}
}
// The largest element is now at index 0
return arr[0];
}
const largest: number = sortAndGetLargest([99, 2, 43, 8, 0, 21, 12]);
console.log(largest); // should be 99, but prints 0

File diff suppressed because it is too large Load Diff

View File

@@ -1,143 +0,0 @@
/*
In this lab, you will develop evaluation rules and typing rules for a small language.
Our language is an extended version of the one discussed in class:
e ::= true
| false
| i (Integers)
| s (Strings)
| succ e
| pred e
| iszero e
| if e then e else e
| concat e e
| isemptystr e
| strlen e
Our types must therefore include:
T ::= Bool
| Int
| String
First, write the evaluation order rules (using small-step semantics) for the expressions in the language.
Next, define the typing rules for these expressions. Be sure that your typing rules guarantee both progress and preservation. (For ways of formally guaranteeing these properties, see Chapter 8 of Ben Pierce's "Types and Programming Languages").
*/
#set page(columns: 2)
// Language keywords rendered in monospace
#let kw(body) = text(font: "DejaVu Sans Mono", size: 0.85em, body)
#let tr = kw[true]
#let fl = kw[false]
#let ife(e1, e2, e3) = [#kw[if] #e1 #kw[then] #e2 #kw[else] #e3]
#let suc(e) = [#kw[succ] #e]
#let prd(e) = [#kw[pred] #e]
#let iszero(e) = [#kw[iszero] #e]
#let concat(e1, e2) = [#kw[concat] #e1 #h(0.2em) #e2]
#let isemptystr(e) = [#kw[isemptystr] #e]
#let strlen(e) = [#kw[strlen] #e]
// Type keywords
#let bool = kw[Bool]
#let int = kw[Int]
#let string = kw[String]
// Small-step evaluation relation: e -> e'
#let sstep(e, ee) = $#e -> #ee$
// Typing relation: e : T
#let tstep(e, T) = $#e : #T$
// Rule name label in small-caps
#let rel(name) = text(size: 0.9em, smallcaps[\[#name\]])
// Rule: fraction with name on the left
// premises (content), conclusion (content)
#let rule(name, premises, conclusion) = {
stack(
dir: ttb,
spacing: 0.4em,
rel(name),
box(stroke: 0.5pt, inset: 4pt, math.equation(
block: true,
numbering: none,
math.frac(premises, conclusion),
)),
)
v(0.8em)
}
= Evaluation Rules
#v(0.6em)
#rule("E-Succ-Ctx", $#sstep[$e$][$e'$]$, $#sstep[#suc[$e$]][#suc[$e'$]]$)
#rule("E-Succ", "", $#sstep[#suc[$i$]][$i + 1$]$)
#rule("E-Pred-Ctx", $#sstep[$e$][$e'$]$, $#sstep[#prd[$e$]][#prd[$e'$]]$)
#rule("E-Pred", "", $#sstep[#prd[$i$]][$i - 1$]$)
#rule("E-IsZero-Ctx", $#sstep[$e$][$e'$]$, $#sstep[#iszero[$e$]][#iszero[$e'$]]$)
#rule("E-IsZero-Zero", "", $#sstep[#iszero[0]][true]$)
#rule("E-IsZero-NonZero", $i != 0$, $#sstep[#iszero[#suc[$i$]]][false]$)
#rule("E-If-Ctx", $#sstep[$e_1$][$e'_1$]$, $#sstep[#ife[$e_1$][$e_2$][$e_3$]][#ife[$e'_1$][$e_2$][$e_3$]]$)
#rule("E-If-True", "", $#sstep[#ife[true][$e_2$][$e_3$]][$e_2$]$)
#rule("E-If-False", "", $#sstep[#ife[false][$e_2$][$e_3$]][$e_3$]$)
#rule("E-Concat-Ctx1", $#sstep[$e_1$][$e'_1$]$, $#sstep[#concat[$e_1$][$e_2$]][#concat[$e'_1$][$e_2$]]$)
#rule("E-Concat-Ctx2", $#sstep[$e_2$][$e'_2$]$, $#sstep[#concat[$s_1$][$e_2$]][#concat[$s_1$][$e'_2$]]$)
#rule("E-Concat", $s_3 = s_1 + s_2$, $#sstep[#concat[$s_1$][$s_2$]][$s_3$]$)
#rule("E-IsEmptyStr-Ctx", $#sstep[$e$][$e'$]$, $#sstep[#isemptystr[$e$]][#isemptystr[$e'$]]$)
#rule("E-IsEmptyStr-Empty", "", $#sstep[#isemptystr[`""`]][true]$)
#rule("E-IsEmptyStr-NonEmpty", [$s !=$ `""`], $#sstep[#isemptystr[$s$]][false]$)
#rule("E-StrLen-Ctx", $#sstep[$e$][$e'$]$, $#sstep[#strlen[$e$]][#strlen[$e'$]]$)
#rule("E-StrLen-Zero", "", $#sstep[#strlen[`""`]][0]$)
#rule("E-StrLen-NonZero", [c is a single char, $s = c + s'$], $#sstep[#strlen[$s$]][#suc[#strlen[$s'$]]]$)
#pagebreak()
= Typing Rules
#v(0.6em)
#rule("T-True", "", $#tstep[true][#bool]$)
#rule("T-False", "", $#tstep[false][#bool]$)
#rule("T-Int", "", $#tstep[n][#int]$)
#rule("T-String", "", $#tstep[s][#string]$)
#rule("T-Succ", $#tstep[$e$][#int]$, $#tstep[#suc[$e$]][#int]$)
#rule("T-Pred", $#tstep[$e$][#int]$, $#tstep[#prd[$e$]][#int]$)
#rule("T-IsZero", $#tstep[$e$][#int]$, $#tstep[#iszero[$e$]][#bool]$)
#rule(
"T-If",
[
$#tstep[$e_1$][#bool]$,
$#tstep[$e_2$][$T$]$,
$#tstep[$e_3$][$T$]$
],
$#tstep[#ife[$e_1$][$e_2$][$e_3$]][T]$,
)
#rule(
"T-Concat",
[
$#tstep[$e_1$][$#string$]$,
$#tstep[$e_2$][$#string$]$
],
$#tstep[#concat[$e_1$][$e_2$]][#string]$,
)
#rule(
"T-IsEmptyStr",
$#tstep[$e$][$#string$]$,
$#tstep[#isemptystr[$e$]][#bool]$,
)
#rule(
"T-StrLen",
$#tstep[$e$][$#string$]$,
$#tstep[#strlen[$e$]][#int]$,
)

View File

@@ -1,113 +0,0 @@
"use strict";
// Matches paterns like '3-10'
const RANGE_PAT = /^(\d+)-(\d+)$/;
// Matches negative index values
const FROM_END_PAT = /^-(\d+)$/;
const NUM_PAT = /^-?\d+$/;
function SmartArray(...args) {
return new Proxy(args, {
get: function(target, prop) {
if (prop.match(RANGE_PAT)) {
// Return a subarray of the elements in the specified range,
// INCLUDING the specified end index.
let start = parseInt(prop.replace(RANGE_PAT, "$1"));
let end = parseInt(prop.replace(RANGE_PAT, "$2")) + 1;
return target.slice(start, end);
} else if (prop.match(FROM_END_PAT)) {
// Return the element at the specified position, counting
// back from target.length. So "-1" will refer to the last
// element in the array.
//
// If the resulting index position is negative,
// raise an exception.
let indexFromEnd = parseInt(prop.replace(FROM_END_PAT, "$1"));
let index = target.length - indexFromEnd;
if (index < 0) {
throw new Error("Index out of bounds");
}
return Reflect.get(target, index);
} else {
// Do the usual array thing -- get the value at the specified index.
return Reflect.get(...arguments);
}
},
set: function(target, prop, newVal) {
// For smart arrays, we only allow updates to numerical fields.
// Throw an exception for a 'prop' that is not a valid integer.
//
// If prop is zero or positive, update the array position normally.
//
// If negative, update the position counting from the end of the array.
// However, raise an exception if the resulting index is still negative.
if (!prop.match(NUM_PAT)) {
throw new Error("Invalid index for assignment");
}
let index = parseInt(prop);
if (index < 0) {
index = target.length + index;
if (index < 0) {
throw new Error("Index out of bounds");
}
}
Reflect.set(target, index, newVal);
return true;
},
deleteProperty: function(target, prop) {
if (!prop.match(NUM_PAT)) {
throw new Error("Invalid index for deletion");
}
let index = parseInt(prop);
if (index < 0) {
index = target.length + index;
if (index < 0) {
throw new Error("Index out of bounds");
}
}
target.splice(index, 1);
return true;
}
});
}
let arr = SmartArray('a', 'b', 'c', 'd', 'e', 'f');
console.log(arr[0]); // a
console.log(arr[4]); // e
console.log(arr['hello']); // undefined
console.log(arr['2-4']); // [c,d,E]
console.log(arr['3-5']); // [d,E,f]
console.log(arr[-1]); // f
console.log(arr[-3]); // d
try {
console.log(arr[-99]);
} catch (e) {
console.log("Exception correctly thrown.");
}
arr[1] = 'B';
console.log(arr[1]); // B
arr[-2] = 'E';
console.log(arr[4]); // E
try {
arr['2-4'] = 'hello';
} catch (e) {
console.log("Exception correctly thrown.");
}
try {
arr[3 * "hello"] = 'hello';
} catch (e) {
console.log("Exception correctly thrown.");
}
delete arr[-2];
console.log(arr);

View File

@@ -1,21 +0,0 @@
let obj = { foo: 'bar' };
let o = new Proxy(obj, {
set: (target, name, val) => {
console.log(`Setting ${name} to ${val}`);
//target[name] = val;
//return true;
//return Reflect.set(target,name,val);
return Reflect.set(...arguments);
},
get: (target, name) => {
console.log(`Getting ${name}`);
return Reflect.get(...arguments);
}
});
o.foo = "fighters";
let ff = "foo" + o.foo;

View File

@@ -1,47 +0,0 @@
"use strict";
// Input: any object.
//
// Returns: An object with a 'revert' method that reverts
// any changes to the specified field.
function makeUndoable(o) {
let oldVals = {};
return new Proxy(o, {
get: (target, name) => {
// Adding a 'revert' method on undoable objects,
// which is not part of the original object.
if (name === 'revert') {
return function(p) {
target[p] = oldVals[p];
}
// If any property other than 'revert' is requested,
// return the value from the underlying object.
} else return target[name];
},
set: (target, name, val) => {
oldVals[name] = target[name];
o[name] = val;
// Returns true to indicate that assignment was successful.
return true;
},
deleteProperty: (target, name) => {
oldVals[name] = target[name];
return delete target[name];
},
});
}
let emp = { fname: "Joe", lname: "Smith", id: 1234, salary: 100 };
emp = makeUndoable(emp);
emp.fname = "Joey";
console.log(`Joe's name accidentally changed to ${emp.fname}`);
emp.revert('fname');
console.log(`Joe's name changed back to ${emp.fname}`);
delete emp.lname;
console.log(`${emp.fname}'s last name is now ${emp.lname}`);
emp.revert('lname');
console.log(`${emp.fname}'s last name is now ${emp.lname}`);

View File

@@ -1,88 +0,0 @@
import Data.Map (Map)
import qualified Data.Map as Map
-- We represent variables as strings.
type Variable = String
data Expression =
ETrue
| EFalse
| EInt Int
| ESucc Expression
| EPred Expression
| EIsZero Expression
| EIf Expression Expression Expression
| ELambda Variable StlcType Expression -- (\x:T.e)
| EApp Expression Expression
| EVar Variable
deriving (Show)
data Value =
VTrue
| VFalse
| VNum Int
deriving (Show)
data StlcType =
TBool
| TInt
| TFun StlcType StlcType -- T1 -> T2
deriving (Show,Eq)
type TypingEnv = Map Variable StlcType
--I'm using zero a lot, so I am making a shortcut
zero = EInt 0
typecheckFail e = error $ "Expression " ++ (show e) ++ " does not typecheck"
typecheck :: Expression -> TypingEnv -> StlcType
typecheck ETrue _ = TBool
typecheck EFalse _ = TBool
typecheck (EInt _) _ = TInt
typecheck e@(ESucc e1) env = case typecheck e1 env of
TInt -> TInt
_ -> typecheckFail e
typecheck e@(EPred e1) env = case typecheck e1 env of
TInt -> TInt
_ -> typecheckFail e
typecheck e@(EIf e1 e2 e3) env =
let t1 = typecheck e1 env
t2 = typecheck e2 env
t3 = typecheck e3 env
in if t1 == TBool && t2 == t3 then t2 else typecheckFail e
typecheck e@(EIsZero e1) env = case typecheck e1 env of
TInt -> TBool
_ -> typecheckFail e
typecheck e@(EVar x) env = case Map.lookup x env of
Just t -> t
Nothing -> typecheckFail e
typecheck e@(ELambda x tin e') env =
let env' = Map.insert x tin env
in TFun tin (typecheck e' env')
typecheck e@(EApp e1 e2) env =
let t1@(TFun tin tout) = typecheck e1 env
t2 = typecheck e2 env
in if t2 == tin then tout else typecheckFail e
--Some sample cases
test1 = typecheck (ESucc zero) Map.empty
test2 = typecheck (EPred (ESucc zero)) Map.empty
test3 = typecheck (EIf ETrue zero (ESucc (ESucc zero))) Map.empty
test4 = typecheck (ELambda "x" TInt ETrue) Map.empty
test5 = typecheck (EApp (ELambda "x" TInt (EIf (EIsZero (EVar "x")) (ESucc zero) zero)) (ESucc zero)) Map.empty
bad1 = typecheck (ESucc EFalse) Map.empty
bad2 = typecheck (EApp (ELambda "x" TInt (EIsZero (EVar "x"))) ETrue) Map.empty
-- main
main = do
print test1
print test2
print test3
print test4
print test5
print bad1
print bad2

View File

@@ -1,326 +0,0 @@
"use strict";
const fs = require('fs');
const opcodes = require('./op-codes.js').opcodes;
const MAX_BUFF_SIZE = 256;
// Constants for types
const LIST = 1;
const OP = 2;
const NUM = 3;
const BOOL = 4;
const VAR = 5;
/**
* The Compiler class is responsible for taking a .scm
* text file and converting it into bytecode format.
*/
class Compiler {
/**
* Constructor.
*/
constructor() {
this.buildMnemonicLookup();
this.varMap = {};
this.varOffset = 0;
}
/**
* Tokenizes a Scheme file, stripping out any comments.
*
* @param {String} contents - Scheme file, as text.
*
* @returns {[String]} - Array of tokens, represented as strings.
*/
tokenize(contents) {
let lines = contents.trim().split('\n')
let tokens = [];
lines.forEach((ln) => {
// Ensuring that parens are always surrounded
// by spaces to simplify parsing.
ln = ln.replaceAll("(", " ( ")
.replaceAll(")", " ) ");
// The comment character in Scheme is ';'
ln = ln.replace(/;.*/, "");
tokens.push(...ln.split(/\s+/).filter(s=>s.length!==0));
});
return tokens;
}
/**
* Parses a stream of tokens, returning an array of objects
* representing the top-level Scheme lists in the program.
* (Note that in Scheme, a list is treated as a function call.)
*
* @param {[String]} tokens - An array of tokens.
*
* @returns {[Object]} - The AST, as a JS object literal.
*/
parse(tokens) {
// The top level AST does not have a type.
let ast = { children: []};
for (let i=0; i<tokens.length; i++) {
let tok = tokens[i];
if (tok === "(") {
let newAst = { parent: ast, type: LIST, children: [] };
ast.children.push(newAst);
ast = newAst;
} else if (tok === ")") {
ast = ast.parent;
} else if (tok.match(/^\d+$/)) {
ast.children.push({ type: NUM, value: parseInt(tok) });
} else if (tok === "#t") {
ast.children.push({ type: BOOL, value: true });
} else if (tok === "#f") {
ast.children.push({ type: BOOL, value: false });
} else if (tok.match(/^\w+$/)) {
ast.children.push({ type: VAR, value: tok });
} else {
ast.children.push({ type: OP, value: tok})
}
}
return ast.children;
}
/**
* Prints out an AST, filtering out circular references.
*
* @param {Object} ast - The AST to print.
*/
printAST(ast) {
console.log(`AST is ${JSON.stringify(ast, (key, value) => {
if (key === 'parent') return value.id;
else return value;
})}`);
}
/**
* Writes a byte to the next position in the bytecode buffer,
* updating the offset to the position for the new write.
*
* @param {Number} byte - A valid byte.
*/
writeByte(byte) {
this.offset = this.bytecode.writeUInt8(byte, this.offset);
}
/**
* Looks up the opcode by its mnemonic and writes it to
* the bytecode buffer.
*
* @param {String} mnemonic - The mnemonic for the opcode.
*/
writeOp(mnemonic) {
let opcode = this.lookupTable[mnemonic];
if (opcode === undefined) {
throw new Error(`The mnemonic ${mnemonic} is not defined.`);
}
this.writeByte(this.lookupTable[mnemonic]);
}
/**
* Converts AST into binary bytecode.
*
* @param {Object} ast - abstract syntax tree of program.
*/
writeBytecode(ast) {
if (ast.type === NUM) {
// Numbers are just pushed on to the stack.
this.writeOp('PUSH1');
this.writeByte(ast.value);
return;
} else if (ast.type === BOOL) {
// Booleans will be stored as either 1 for true, or as a 0 for false.
this.writeOp('PUSH1');
this.writeByte(ast.value ? 1 : 0);
return;
} else if (ast.type === VAR) {
// We look up the offset for a variable and push the offset
// value on to the stack. The 'MLOAD' operation will
// retrieve the value stored at that position in the memory.
this.writeOp('PUSH1');
this.writeByte(this.varMap[ast.value]);
this.writeOp('MLOAD');
return;
}
// If we made it hear, we have a list.
// The first argument is the name of the 'function'
// that we will be invoking.
let first = ast.children[0];
// Almost all functions need some special handling for the
// first argument. Some functions will need the additional
// arguments stored in 'rest'.
let second = ast.children[1];
let rest = ast.children.slice(2);
switch (first.value) {
case "println":
this.writeBytecode(second);
this.writeOp('PRINT');
break;
case "define":
// The define function lets us store variables.
//
// The variable name is stored in 'second.value'.
// Update the 'this.varMap' array to store the current
// value of 'this.varOffset'.
//
// The VM will need to push the value on to the stack,
// push 'this.varOffset' on to the stack, and then
// invoke 'MSTORE'.
//
// Increment this.varOffset so that it points to the next
// position in memory.
this.varMap[second.value] = this.varOffset;
rest.forEach((x) => {
this.writeBytecode(x);
});
this.writeOp('PUSH1');
this.writeByte(this.varOffset);
this.writeOp('MSTORE');
this.varOffset++;
break;
case "if":
// EXTRA CREDIT!
// Add support for if expressions.
// The cond.scm file gives you some good examples.
this.writeBytecode(second);
// get the offset program counter to change it later
this.writeOp('PUSH1');
const counterOffset = this.offset;
this.writeByte(0x00);
this.writeOp('JUMPI');
// else
this.writeBytecode(rest[1]);
// change this to after the then
this.writeOp('PUSH1');
const elseOffset = this.offset;
this.writeByte(0x00);
this.writeOp('JUMP');
// then
const thenOffset = this.offset;
this.writeOp('JUMPDEST');
this.writeBytecode(rest[0]);
const endOffset = this.offset;
this.writeOp('JUMPDEST');
// update the offsets
this.bytecode[counterOffset] = thenOffset;
this.bytecode[elseOffset] = endOffset;
break;
case "+":
this.writeBytecode(second);
rest.forEach((x) => {
this.writeBytecode(x);
this.writeOp('ADD');
});
break;
case "*":
// Using the '+' case as a template, add support
// for '*'. Note that the 'MUL' opcode only works
// with two arguments, whereas '*' allows an arbitrary
// number of arguments.
this.writeBytecode(second);
rest.forEach((x) => {
this.writeBytecode(x);
this.writeOp('MUL');
});
break;
case "-":
// Add support for '-'. The approach here will be
// Similar to the solution for '+' and '*'. However,
// one key difference is that the order of the arguments
// matters. You will need to use 'SWAP1' to get the
// arguments ordered correctly before invoking 'SUB'.
this.writeBytecode(second);
rest.forEach((x) => {
this.writeBytecode(x);
this.writeOp('SWAP1');
this.writeOp('SUB');
});
break;
default:
throw new Error(`Unexpected head: '${first.value}'`);
}
}
/**
* Builds up a mapping of opcode mnemonics to the corresponding
* hexadecimal values.
*/
buildMnemonicLookup() {
this.lookupTable = {};
Object.keys(opcodes).forEach((opcode) => {
let inst = opcodes[opcode];
this.lookupTable[inst.mnemonic] = opcode;
});
}
/**
* This method takes a scheme file, tokenizes and parses it,
* and finally compiles it to binary bytecode.
*
* @param {String} fileName - The name of the scheme file.
*
* @returns {String} - The name of the bytecode file.
*/
compileScheme(fileName) {
if (!fileName.toLowerCase().endsWith('.scm')) {
throw new Error(`${fileName} does not end with a .scm extension.`);
}
fs.readFileSync(fileName);
let contents = fs.readFileSync(fileName, 'utf8');
let tokens = this.tokenize(contents);
let asts = this.parse(tokens);
// The bytecode size cannot be larger than MAX_BUFF_SIZE.
this.bytecode = Buffer.alloc(MAX_BUFF_SIZE);
// The offset tracks the current position in the bytecode buffer.
this.offset = 0;
asts.forEach((ast) => {
this.writeBytecode(ast);
});
// The output file will have the same name as the input file,
// except that '.scm' will be replaced with '.byco'.
let outputFile = fileName.replace(/.scm\b/i, ".byco");
fs.writeFileSync(outputFile, this.bytecode.slice(0, this.offset, 'hex'));
return outputFile;
}
}
// Handling command line arguments.
if (process.argv0 === 'node') {
process.argv.shift();
}
if (process.argv.length !== 2) {
console.log("compiler.js <scheme file>");
process.exit(1);
}
let cmplr = new Compiler();
let scmFile = process.argv[1];
console.log(`Compiling ${scmFile}...`);
let bytecodeFile = cmplr.compileScheme(scmFile);
console.log(`Bytecode written to ${bytecodeFile}.`);

Binary file not shown.

View File

@@ -1,16 +0,0 @@
(println #t)
(if #t (println 3) (println 4))
(if #f (println 3) (println 4))
; PUSH1 1
; PUSH1 offset_to_then
; JUMPI
;
; #else
; PUSH1 offset_to_end
; JUMP
;
; JUMPDEST @offset_to_then
; #then
;
; JUMPDEST @offset_to_end

View File

@@ -1,64 +0,0 @@
'use strict';
let opcodes = {
0x01: { mnemonic: 'ADD', evaluate: (vm) => {
let v1 = vm.stack.pop();
let v2 = vm.stack.pop();
vm.stack.push(v1+v2);
}},
0x02: { mnemonic: 'MUL', evaluate: (vm) => {
// Pop the top two arguments off of the stack,
// and then push the result on to the stack.
let v1 = vm.stack.pop();
let v2 = vm.stack.pop();
vm.stack.push(v1*v2);
}},
0x03: { mnemonic: 'SUB', evaluate: (vm) => {
let v1 = vm.stack.pop();
let v2 = vm.stack.pop();
vm.stack.push(v1-v2);
}},
0x52: { mnemonic: 'MLOAD', evaluate: (vm) => {
let offset = vm.stack.pop();
vm.stack.push(vm.memory[offset]);
}},
0x53: { mnemonic: 'MSTORE', evaluate: (vm) => {
let offset = vm.stack.pop();
let value = vm.stack.pop();
vm.memory[offset] = value;
}},
0x56: { mnemonic: 'JUMP', evaluate: (vm) => {
let offset = vm.stack.pop();
vm.pc = offset;
}},
0x57: { mnemonic: 'JUMPI', evaluate: (vm) => {
let offset = vm.stack.pop();
let condition = vm.stack.pop();
if (condition) {
vm.pc = offset;
}
}},
0x5B: { mnemonic: 'JUMPDEST', evaluate: (vm) => {
// Does nothing. We could check to make sure that jumps
// always land at JUMPDEST opcodes, but it is not totally
// clear that it is worth the bother.
}},
0x60: { mnemonic: 'PUSH1', evaluate: (vm) => {
// The next byte is data, not another instruction
vm.pc++;
let v = vm.bytecode.readUInt8(vm.pc);
vm.stack.push(v);
}},
0x90: { mnemonic: 'SWAP1', evaluate: (vm) => {
let v1 = vm.stack.pop();
let v2 = vm.stack.pop();
vm.stack.push(v1);
vm.stack.push(v2);
}},
0x0c: { mnemonic: 'PRINT', evaluate: (vm) => {
// **NOTE**: This is not a real EVM opcode.
console.log(vm.stack.pop());
}},
};
exports.opcodes = opcodes;

View File

@@ -1 +0,0 @@
`

View File

@@ -1,3 +0,0 @@
; Simple test that prints 3.
(println 3)

Binary file not shown.

View File

@@ -1,5 +0,0 @@
(define x 3)
(define y (+ x 1))
(println (- x 2))
(println (* x y))

View File

@@ -1 +0,0 @@
```

View File

@@ -1,2 +0,0 @@
(println (+ 1 2 3))

View File

@@ -1,2 +0,0 @@
``` `
``<02> `

View File

@@ -1,4 +0,0 @@
(println (+ 2 3 4))
(println (- 13 (* 2 4)))
(println (- 10 4 3))

View File

@@ -1,68 +0,0 @@
'use strict';
const fs = require('fs');
const opcodes = require('./op-codes.js').opcodes;
/**
* The VM is responsible for executing the bytecode format.
*/
class VirtualMachine {
/**
* Initializes the virtual machine with the specified amount of
* gas. The stack and memory are both initially empty.
*
* @param {Number} gas - Amount of gas the VM begins with.
*/
constructor() {
this.stack = [];
this.memory = [];
}
/**
* Loads a bytecode file and returns an array of strings,
* which are the commands within the file.
*/
static loadBytecode(bytecodeFile) {
let contents = fs.readFileSync(bytecodeFile);
return Buffer.from(contents);
}
/**
* Evaluates the specified file.
*/
evaluate(bytecodeFile) {
this.bytecode = this.constructor.loadBytecode(bytecodeFile);
// Initializing the program counter to keep track of our
// place within the program.
this.pc = 0;
while (this.pc < this.bytecode.length) {
let opcode = this.bytecode.readUInt8(this.pc);
//console.log(`Evaluating ${opcode.toString(16)}`);
let operation = opcodes[opcode];
if (operation === undefined) {
throw new Error(`Unable to find instruction for ${opcode.toString(16)}`);
}
operation.evaluate(this);
this.pc++;
}
}
}
// Handling command line arguments.
if (process.argv0 === 'node') {
process.argv.shift();
}
if (process.argv.length !== 2) {
console.log("vm.js <bytecode file>");
process.exit(1);
}
let bytecodeFile = process.argv[1];
console.log(`Executing ${bytecodeFile}...`);
let vm = new VirtualMachine();
vm.evaluate(bytecodeFile);

View File

@@ -1,114 +0,0 @@
#! /usr/bin/env ruby -w
#Represents a Rogerian psychiatrist
class Shrink
#initializes 'memory' of Eliza.
def initialize()
@he="he"
@she="she"
@they="they"
end
#read a statement and convert it to a psychiatric response.
def generateResponse(blather)
#downcase for ease of substitution
blather = blather.downcase
# filter out words like "well" or "perhaps" from the beginning
blather.gsub!(/^(well|perhaps|anyway|actually),?\s+/i, '')
# change 'you', 'your', etc. to uppercase 'I', 'MY'
blather.gsub!(/\byour\b/,"MY")
blather.gsub!(/\byou\b/,'I')
#Replace 'my' with 'your', 'me' with 'you', 'I' with 'you', etc.
blather.gsub!(/\bmy\b/,"your")
blather.gsub!(/\bme\b/,"you")
blather.gsub!(/\bi\b/,'you')
#Get future references -- note that these do NOT change the immediate output
hePat=/.*\b(your (father|brother|(ex-?)?(husband|boyfriend)))\b.*/
shePat = /.*\b(your (mother|sister|(ex-?)?(wife|girlfriend)))\b.*/
theyPat = /.*\b(your (parents|friends|siblings|colleagues|classmates))\b.*/
@he=blather.sub(hePat, '\1').chomp if blather =~ hePat
@she=blather.sub(shePat, '\1').chomp if blather =~ shePat
@they=blather.sub(theyPat, '\1').chomp if blather =~ theyPat
# handle "always" and "never" responses
if blather =~ /\b(always|never)\b/
return "CAN YOU BE MORE SPECIFIC?"
end
# handle "are you" questions
if blather =~ /^are I (.+?)\?*$/
return "IS IT IMPORTANT IF I AM #{$1.upcase}?"
end
#Sub in past references, but only for the 1st occurrence or it looks weird
blather.sub!(/\b(he|him)\b/, @he)
blather.sub!(/\b(she|her)\b/, @she)
blather.sub!(/\bthey\b/, @they)
#Deal with name
namePat=/.*\byour name is (\w+).*/
@name=blather.sub(namePat,'\1')
blather.sub!(namePat,'nice to meet you, \1. How can I help you')
# add some empathy for sad words
if blather =~ /\b(sad|depressed|upset|hurt|angry|frustrated)\b/
empathetic_responses = [
"THAT SOUNDS DIFFICULT. ",
"I CAN HEAR THE PAIN IN YOUR WORDS. ",
"THAT MUST BE HARD FOR YOU. "
]
prefix = empathetic_responses.sample
return prefix + "TELL ME MORE ABOUT " + blather.upcase + "?"
end
#results are uppercased, for aesthetics.
return blather.upcase + "?"
end
end
#main -- reads from standard input unless -test is the first parameter.
eliza = Shrink.new()
if ARGV[0] == "-test"
['My girlfriend never listens to me',
"I think she might be deaf",
"yes",
"I am afraid of clowns",
"Well, they just seem creepy",
"Also, when I was a kid, a clown killed my dad",
"Are you a clown in disguise?",
# test "always" response
"I always feel nervous",
# test "never" response
"My parents never understand me",
# test "Are you" questions
"Are you listening to me?",
"Are you a real doctor?",
# test word filtering
"Perhaps my friends don't like me",
"Well, I suppose that makes sense",
"Actually, I think I'm getting better",
# test "they" memory
"My colleagues are very competitive",
"They make me feel inadequate",
"Sometimes they ignore my ideas",
# Test empathetic responses
"I am feeling very sad today",
"My boss makes me frustrated",
"I feel so depressed lately",
].each do |stmt|
puts stmt
puts eliza.generateResponse(stmt)
puts
end
else
while line = gets
response = eliza.generateResponse line
puts response
end
end

View File

@@ -1,52 +0,0 @@
def do_noisy
puts "About to call block"
yield
puts "Just called block"
end
do_noisy do
puts 3 + 4
end
class Array
def each_downcase
self.each do |word|
yield word.downcase
end
end
end
["Alpha", "Beta", "AndSoOn"].each_downcase do |word|
puts word
end
def iff b
return if b == false
yield
end
def test_block
iff true do
puts "hello block"
return
end
puts "goodbye block"
end
def iff2 b, lam
return if b == false
lam.call
end
def test_lambda
iff2 true, (lambda do
puts "hello lambda"
return
end)
puts "goodbye lambda"
end
test_block
test_lambda

View File

@@ -1,25 +0,0 @@
def conversion_chart(from_units, to_units, values)
puts "#{from_units}\t#{to_units}"
left_line = right_line = ""
from_units.length.times { left_line += '-' }
to_units.length.times { right_line += '-' }
puts "#{left_line}\t#{right_line}"
for val in values
converted = yield val
puts "#{val}\t#{converted}"
end
puts
end
celsius_temps = [0,10,20,30,40,50,60,70,80,90,100]
conversion_chart("C", "F", celsius_temps) {|cel| cel * 9 / 5 + 32}
fahrenheit_temps = [0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200 ]
conversion_chart("Fahr.", "Celsius", fahrenheit_temps) {|fahr| (fahr-32) * 5 / 9 }
conversion_chart("Km", "Miles", (1..10)) do |km|
mile = 0.621371 * km
end

View File

@@ -1,42 +0,0 @@
class Employee
# Adding methods to the singleton class of the object
# Employee (which has the class Class).
class << self
def add(emp)
puts "Adding #{emp}"
@employees = Hash.new unless @employees
@employees[emp.name] = emp
end
def get_emp_by_name name
@employees[name]
end
end
#####################################################
attr_accessor :name, :ssid, :salary
def initialize(name, ssid, salary)
@name = name
@ssid = ssid
@salary = salary
Employee.add self
end
def to_s
@name
end
end
alice = Employee.new("Alice Alley", 1234, 75000);
bob = Employee.new("Robert Tables", 5678, 50000);
class << bob
def signing_bonus
2000
end
end
puts(bob.signing_bonus);
#puts(alice.signing_bonus);
b = Employee.get_emp_by_name "Robert Tables"
puts b.signing_bonus

View File

@@ -1,19 +0,0 @@
class Class
def my_attr_accessor(*args)
args.each do |prop|
# Creating the getter
self.class_eval("def #{prop}; @#{prop}; end")
# Creating the setter
self.class_eval("def #{prop}=(v); @#{prop}=v; end")
end
end
end
class Musician
my_attr_accessor :name, :genre, :instrument
end
m = Musician.new
m.name = "Norah Jones"
puts m.name

View File

@@ -1,21 +0,0 @@
// Assume that this string comes from across the network
// representing employee records.
var jsonStr =
"[{name: 'Philip J. Fry', age: 1000, job: 'delivery boy'}," +
" {name: (function(){console.log('***All glory to the Hypnotoad!***')})() }," +
" {name: 'Bender Rodriguez', age: 42, job: 'bending unit'}]";
var employeeRecords = eval(jsonStr);
for (var i in employeeRecords) {
var emp = employeeRecords[i];
console.log(emp.name);
}
/*
$ node eval.js
***All glory to the Hypnotoad!***
Philip J. Fry
undefined
Bender Rodriguez
*/

View File

@@ -1,67 +0,0 @@
# Ruby has rich metaprogramming tools, often bowered from Smalltalk.
# Like JavaScript, it has an eval feature.
prog = "puts 3 + 4"
eval prog
# Eval is one of the most powerful metaprogramming features,
# but it is also one of the most dangerous.
print "Please enter your method name: "
m = gets.chomp
eval "def #{m}; puts 'Hi!'; end"
eval m
=begin
Thomass-MacBook-Pro-3:lab15 taustin$ ruby eval.rb
Please enter your method name: abc; end; puts "Mwah, hah, hah!"; #
Mwah, hah, hah!
eval.rb:11: (eval):1: compile error (SyntaxError)
(eval):1: syntax error, unexpected kEND, expecting $end
abc; end; puts "Mwah, hah, hah!"; #
-----------------------
Eval is horribly abused (Richards et al. 2011, See the Eval that men do,), but it is useful at times.
For instance, in JavaScript, it served as an early (but unsafe) version of JSON.parse.
Similar to goto, it is a powerful but dangerous construct, and is often used in places
where the language is missing a key feature.
"A design pattern is the sincerest form of feature request".
However, it does not tend to show up as often in Ruby.
In part, Ruby has some safer alternatives that are nearly as powerful.
They take blocks rather than expressions.
=end
# instance_eval -- used for prying open objects to get at their private data.
# This can be handy for things like writing a debugger.
class Person
attr_reader :name
def initialize name
@name = name
end
end
bob = Person.new "Robert"
puts bob.name
#bob.name = "Bobby" # Error
bob.instance_eval do
@name = "Bobby"
end
puts bob.name
# And finally class_eval/module_eval, which serve as an alternate way of opening up a class.
favorite_song = "Streets of Laredo"
class Person
#puts favorite_song # error
end
Person.class_eval do
puts favorite_song
#def sing
# puts "When #{@name} went out in the #{favorite_song}..." # Will not see favorite_song
#end
define_method "sing" do
puts "When #{@name} went out in the #{favorite_song}..."
end
end
bob.sing

View File

@@ -1,30 +0,0 @@
=begin
Update the Record class so that updates with either
a tainted name or a tainted value are ignored.
Do this first by explicitly checking the taint on a field.
Would this be sufficient if an attacker could control part of the code?
If not, how could the different taint modes be useful?
=end
class Record
def initialize fields
@fields = fields
end
def set_property name, value
@fields[name] = value
end
def get_property name
@fields[name]
end
end
r = Record.new 'fname' => 'Rick', 'lname' => 'Grimes', 'profession' => 'Police Officer'
r.set_property 'profession'.taint, 'Zombie Hunter'
r.set_property 'lname', 'Smith'.taint
p r.get_property 'profession'
p r.get_property 'lname'

View File

@@ -1,21 +0,0 @@
class Person
attr_accessor :name
def initialize(name)
@name = name
end
def make_introduction
puts "Hi, my name is #{@name}. Nice to meet you."
end
def method_missing(m)
puts "Didn't understand #{m}"
end
end
alice = Person.new('Alice')
alice.make_introduction
alice.foo

View File

@@ -1,37 +0,0 @@
=begin
Ruby provides a number of hooks that allow a developer
to change the behavior of objects.
The design is similar to JavaScript's Proxies (both are
metaobject protocols), but unlike proxies,
these hooks apply for all objects.
The most famous of these is method_missing,
based on Smalltalk's doesNotUnderstand:.
=end
# Consider Ruby on Rails.
# With Rails you refer to a record's fields by their names.
# We will (crudely) simulate that.
class Record
def initialize fields
@fields = fields
end
def method_missing m, *args
meth_name = m.to_s
if (meth_name.end_with?("=")) then
@fields[meth_name.chop] = args[0]
else
@fields[meth_name]
end
end
end
r = Record.new ({ 'fname' => 'Rick', 'lname' => 'Grimes', 'profession' => 'Police Officer' })
puts r.profession
r.profession = 'Zombie hunter'
puts r.profession
# Ruby has const_missing as well, which works in a similar manner, except that it applies
# to missing class constants

View File

@@ -1,56 +0,0 @@
class Tree
attr_accessor :value, :left, :right
def initialize(value, left=nil, right=nil)
@value = value
@left = left
@right = right
end
def each_node(&block)
block.call(@value)
@left.each_node(&block) if @left
@right.each_node(&block) if @right
end
def method_missing(method_name, *args)
method_name = method_name.to_s
path = method_name.scan(/(left|right)/).flatten
current_node = self
path.each do |direction|
if direction == "left"
current_node = current_node.left
elsif direction == "right"
current_node = current_node.right
end
return nil if current_node.nil?
end
return current_node.value
super
end
end
my_tree = Tree.new(42,
Tree.new(3,
Tree.new(1,
Tree.new(7,
Tree.new(22),
Tree.new(123)),
Tree.new(32))),
Tree.new(99,
Tree.new(81)))
my_tree.each_node do |v|
puts v
end
arr = []
my_tree.each_node do |v|
arr.push v
end
p arr
p "Getting nodes from tree"
p my_tree.left_left
p my_tree.right_left
p my_tree.left_left_right
p my_tree.left_left_left_right

View File

@@ -1,20 +0,0 @@
def with_prob (prob)
yield if (Random.rand < prob)
end
with_prob 0.42 do
puts "There is a 42% chance that this code will print"
end
def foo x
with_prob 0.5 do
puts "Executing with_prob block"
return 0
end
return x
end
puts "Foo is #{foo 1}"

View File

@@ -1,17 +0,0 @@
function withProb(prob, f) {
if (Math.random() < prob) {
return f();
}
}
function foo(x) {
withProb(0.5, function() {
console.log("Execution withProb callback");
return 0;
});
return x;
}
console.log("Foo is " + foo(1));