Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
320817c8a1
|
|||
|
4aa6836dab
|
|||
|
8a13b621bf
|
|||
|
278f9dce2e
|
|||
|
64c43ed46f
|
|||
|
a2e3a80c61
|
|||
|
ba60218766
|
|||
|
10d551b4bc
|
|||
|
16be5b2691
|
|||
|
4cda0266e1
|
|||
|
10451c7cdd
|
|||
|
fa77c90d1b
|
|||
|
b9e10c3e37
|
|||
|
ca9f9baf8b
|
|||
|
e8f7818ec9
|
|||
|
14dc6dee28
|
@@ -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.
@@ -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}
|
||||
\]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
X := 0 - 3;
|
||||
if X < 0 then
|
||||
X := 0 - X
|
||||
else
|
||||
skip
|
||||
endif
|
||||
@@ -1,7 +0,0 @@
|
||||
x := 7 + 3;
|
||||
if x then
|
||||
true
|
||||
else
|
||||
false
|
||||
endif
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
X := 3 + 9 * 4;
|
||||
Y := 5 * 2 + 7;
|
||||
Z := 360 / 12 / 3
|
||||
|
||||
11
hw3/fact.imp
11
hw3/fact.imp
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
17
hw3/test.hs
17
hw3/test.hs
@@ -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"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
X := ( ( 1 + 2 ) - 3 ) + ( 1 + 3 );
|
||||
Y := 0;
|
||||
while X>0 do
|
||||
Y := Y+X;
|
||||
X := X-1
|
||||
endwhile
|
||||
@@ -1,7 +0,0 @@
|
||||
X := 10;
|
||||
Y := 3;
|
||||
Z := 0;
|
||||
while X > 0 do
|
||||
Z := Z + Y;
|
||||
X := X - 1
|
||||
endwhile
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
[
|
||||
42,
|
||||
33,
|
||||
71
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
['hi',"how",'are','u',null]
|
||||
@@ -1,4 +0,0 @@
|
||||
Year,Make,Model,Length
|
||||
1997,Ford,E350,2.34
|
||||
2000,Mercury,Cougar,2.38
|
||||
|
||||
|
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
125
lab11/intro.js
125
lab11/intro.js
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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]$,
|
||||
)
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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
|
||||
@@ -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}.`);
|
||||
BIN
lab17/cond.byco
BIN
lab17/cond.byco
Binary file not shown.
@@ -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
|
||||
@@ -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;
|
||||
@@ -1 +0,0 @@
|
||||
`
|
||||
@@ -1,3 +0,0 @@
|
||||
; Simple test that prints 3.
|
||||
(println 3)
|
||||
|
||||
BIN
lab17/store.byco
BIN
lab17/store.byco
Binary file not shown.
@@ -1,5 +0,0 @@
|
||||
(define x 3)
|
||||
(define y (+ x 1))
|
||||
(println (- x 2))
|
||||
(println (* x y))
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
```
|
||||
@@ -1,2 +0,0 @@
|
||||
(println (+ 1 2 3))
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
````
|
||||
``<02>`
|
||||
@@ -1,4 +0,0 @@
|
||||
(println (+ 2 3 4))
|
||||
(println (- 13 (* 2 4)))
|
||||
(println (- 10 4 3))
|
||||
|
||||
68
lab17/vm.js
68
lab17/vm.js
@@ -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);
|
||||
114
lab18/eliza.rb
114
lab18/eliza.rb
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
42
lab19/emp.rb
42
lab19/emp.rb
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user