Модераторы: LSD

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Попытка сравнить C/Java/C#/Python 
:(
    Опции темы
Sardar
Дата 21.1.2008, 22:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 2
Всего: 317



Когда то тут было. По началу немного сумбурно, тема была вырезана из другого топа smile

Лаба из прошлого блока, читает и выполняет FEM - простейший абстрактный функциональный язык (всего три операции). На вкус отвратна (специально без оптимизаций, учили рекурсию и partial application функций), писалась после первых недель знакомства с Haskell, но принцип понять можно smile
Код
{---------------------------------------------
 - FEM language implementation
 - Sardar Yumatov (1679848) and others (1609874)
 -
 - Usage: showw (parseFEMProgram FEM_Code >>= eval)
 - Example: showw (parseFEMProgram "foo a bb = a, ((T foo S P)) 5 6 7" >>= eval)
 ---------------------------------------------}
module FEM where
import List
import Char


type FemGetal = Int                -- Fem deals with numbers only
type FemVariabele = String            -- Any identifier
type CodePosition = (Int,Int)            -- Term position in the source file, can be extended with file name later



-- собсно AST, без лишних классов
---------- Parsed program representation --------------
data FemTerm =
      S            -- increment, native function
    | P            -- decrement, native function
    | T            -- test (if-else), native function
    | N FemGetal        -- constant (integer) value
    | V FemVariabele    -- recall variable
    | L [FemParsedTerm]    -- execute (apply) variable (function)


type FemParsedTerm = (FemTerm, CodePosition)                -- term annotated with position in source code
type FemDeclaratie = (FemVariabele, (CodePosition, [FemVariabele], FemParsedTerm))    -- function declaration
type FemProg = ([FemDeclaratie], FemParsedTerm)                -- FEM program definition


---------- Token stream (tokenizer) ------------------
data Token = HOpen | HClose | Comma | Equal | Natural FemGetal | Variable String | PToken | TToken | SToken
type TokenStream = [(Token, CodePosition)]


---------- Runtime program representation ------------
data FemRuntimeTerm =
      NR FemGetal                                            -- primitive value
    | LR CodePosition [FemArgument] [FemParsedTerm]                            -- dellayed L term
    | FR String CodePosition [FemArgument] [FemVariabele] (Either FemParsedTerm FemSystemCallback)    -- partially applied function

-- Lookup must not be a stack, the context always consist of top-level functions and current arguments set
type ContextLookup = ([FemDeclaratie], [FemArgument])
type FemArgument = (FemVariabele, FemRuntimeTerm)

-- Native function callback.
type FemSystemCallback = [FemDeclaratie] -> [FemArgument] -> (Result FemRuntimeTerm)



----------------- Error handling -----------------------
-- Result of any evaluation (Maybe like type, extended with error messages)
data Result a = Error String | Success a

-- Allows chaining multiple (a -> Result a) together
instance Monad Result where
    Error  msg >>= f = Error msg
    Success rs >>= f = f rs
    Error  msg >> _  = Error msg
    _ >> Error msg   = Error msg
    Success a  >> Success b = Success b
    return          = Success
    fail msg        = Error msg

------------------- Pretty prints --------------------
class Source a where
    toSource :: a -> String

instance Show FemTerm where
    show a = toSource a

instance Source FemTerm where
    toSource S               = "S"
    toSource P               = "P"
    toSource T               = "T"
    toSource (N n)           = "N " ++ (show n)
    toSource (V str)         = "V " ++ str
    toSource (L trms)        = "(" ++ concat (intersperse " " (map (toSource.fst) trms)) ++ ")"

instance Show Token where
    show HOpen          = "_(_"
    show HClose         = "_)_"
    show Comma          = ", "
    show Equal          = " = "
    show PToken         = "p "
    show TToken         = "t "
    show SToken         = "s "
    show (Natural int)  = "@" ++ (show int)
    show (Variable str) = "$" ++ str

-- for debugging only
instance Show FemRuntimeTerm where
    show (NR n) = "NR " ++ (show n)
    show (LR p ctx trms) = "(" ++ concat (intersperse " " (map show trms)) ++ ")"
    show (FR nm p args ac func) = "Func: " ++ nm


-- Convert compiled FEM program to source
toSourceProgram :: FemProg -> String
toSourceProgram (xs, (term, q)) = ("(\n\t" ++ (concat (intersperse ",\n\t" (map toSourceDecl xs))) ++ "\n,\n\t" ++ (toSource term))
    where    toSourceDecl (name, (cp, xs, term)) = (name ++ " " ++ (concat (intersperse " " xs)))

-- Retrive from Result monad
showw :: (Show a) => (Result a) -> String
showw (Success foo) = (show foo)
showw (Error foo) = (show foo)

-- Print term position. Used in error messages.
showPosition (a, b) = "L" ++ (show a) ++ ":C" ++ (show b)





--------------------------- Parser -----------------------------
-- Parse FEM program into an exutable
parseFEMProgram :: String -> Result FemProg
parseFEMProgram code = tokenize code >>= trimBrackets >>= shredTokens >>= parseParts
    where    parseParts (decl, term) = parseDeclarations ([], decl) >>= parseTerm term
        parseTerm term (decls, []) = parseTermStream term >>= assembleProgram decls
        assembleProgram decls (term, []) = return (decls, term)
        assembleProgram decls (term, (x:strm)) = fail ("Unexpected input after closing ')' at the end of the program: " ++ (show x))
        trimBrackets ((HOpen, p):ts) =
                       case reverse ts of
                               ((HClose,p):tts) -> return (reverse tts)
                               (tts)            -> fail "Global ) at the end of the code is required!"
        trimBrackets ts = return ts


-- Split FEM source code to tokens.
-- This function will parse the whole string at once (no lazy evaluation), in return
-- the function will fail correctly if given FEM code contains syntax errors.
tokenize :: String -> Result TokenStream
tokenize src = tryNextToken ([], (0,0), src) >>= returnStream
    where    returnStream (toks, p, rest) = return toks
        tryNextToken (toks, p, []) = return (toks, p, [])
        tryNextToken (toks, p, xs) = (nextToken p xs >>= addTok) >>= tryNextToken
            where    addTok (tok, pp, pn, rest) = return (toks ++ [(tok, pp)], pn, rest)

-- Read next token
nextToken :: CodePosition -> String -> Result (Token, CodePosition, CodePosition, String)
nextToken p@(r,c) (x:xs)
    | x == '='               = return (Equal, p, (r, c+1), xs)
    | x == '('               = return (HOpen, p, (r, c+1), xs)
    | x == ')'               = return (HClose, p, (r, c+1), xs)
    | x == ','               = return (Comma, p, (r, c+1), xs)
    | x == '\n'              = nextToken (r+1, 0) xs
    | elem x "\t \r"           = nextToken (r, c+1) xs
    | isAlpha x || isDigit x = tryToken p (break (not.isAlphaNum) (x:xs))
    | otherwise            = fail ("Not a valid FEM token. Unexpected: " ++ [x])


-- Try to parse a Variable, Natural, P, S or T Token from a String.
tryToken :: CodePosition -> (String, String) -> Result (Token, CodePosition, CodePosition, String)
tryToken p@(r,c) (term, rest)
    | term == "p" || term == "P" = return (PToken, p, (r, c+1), rest)
    | term == "s" || term == "S" = return (SToken, p, (r, c+1), rest)
    | term == "t" || term == "T" = return (TToken, p, (r, c+1), rest)
    | all isDigit term           = return (Natural (read term), p, (r, c+(length term)), rest)
    | isAlpha (head term)        = return (Variable term, p, (r, c+(length term)), rest)
    | otherwise                  = fail ("Not a valid number: " ++ term)



-- Split token stream to program declarations and the main term.
shredTokens :: TokenStream -> Result ([TokenStream], TokenStream)
shredTokens (x:xs) = return (decl, term)
    where    (term:decl) = map reverse (shred (reverse (x:xs)))
        shred [] = []
        shred ((Comma, p):ts) = shred ts    -- so (decl, , , term) is allowed, commas are ignored
        shred ts = y : shred ys
            where    (y, ys) = break isComma ts
                isComma (Comma, p) = True
                isComma _          = False


-- Parse list of variable declarations
parseDeclarations :: ([FemDeclaratie], [TokenStream]) -> Result ([FemDeclaratie], [TokenStream])
parseDeclarations (dcl, []) = return (dcl, [])
parseDeclarations (dcl, (s:sms)) = parseDeclaration s >>= addDeclaration
       where   addDeclaration decl = (return (decl:dcl, sms)) >>= parseDeclarations



-- Parse FEM declaration (variable)
parseDeclaration :: TokenStream -> Result FemDeclaratie
parseDeclaration []  = fail "Software bug detected: empty variable declaration. Probably parser's (shredTokens) error!"
parseDeclaration str = possibleBrakets str >>= combineResults
    where    combineResults ((nm,p), arg, term, []) = return (nm, (p, arg, term))
        combineResults ((nm,pp), arg, term, ((t,p):ts)) = fail ((showPosition p) ++ " - Unexpected token after closing ')' of variable declaration: " ++ nm)

        possibleBrakets ((HOpen, p):ts) = (parseDecl p ts) >>= expectClose    -- consume (
        possibleBrakets ((t,p):ts) = parseDecl p ((t,p):ts)                -- no brakets variant

        expectClose (n,a,t,[(HClose,p)]) = return (n,a,t,[])        -- consume )
        expectClose ((n,p),a,t,[]) = fail ((showPosition p) ++ " - Expecting ')' at the end of variable declaration: " ++ n)
        expectClose ((n,pp),a,t,((o,p):ts)) = fail ((showPosition p) ++ " - Expecting only ')' at the end of variable declaration: " ++ n)

        parseDecl p [] = fail ((showPosition p) ++ " - Expecting variable declaration, but there is no more terms.")
        parseDecl p ts = parseName ts >>= parseArguments >>= parseBody

        parseName (((Variable nm), p):str) = return ((nm,p), str)
        parseName ((t,p):xs) = fail ((showPosition p) ++ " - fuck Expecting variable name, got: " ++ (show t))

        parseArguments ((n, p), str) = nextArgument ([], str) >>= combineArg
            where    combineArg (arg, ts) = return ((n,p), arg, ts)
                nextArgument (arg, []) = fail ((showPosition p) ++ " - Expecting body for variable: " ++ n)
                nextArgument (arg, (((Variable nm), p):ts)) = (return (arg ++ [nm], ts)) >>= nextArgument
                nextArgument (arg, ((Equal, p):ts)) = return (arg, ts)
                nextArgument (arg, ((t, pt):ts)) = fail ((showPosition pt) ++ " - Expecting variable name or '=', got: " ++ (show t))

        parseBody ((n,p), a, []) = fail ((showPosition p) ++ " - Expecting body (code) for variable: " ++ n)
        parseBody (n, a, str) = parseTermStream str >>= returnBody
            where    returnBody (term, rest) = return (n, a, term, rest)


-- Parse FEM term (program body)
parseTermStream :: TokenStream -> Result (FemParsedTerm, TokenStream)
parseTermStream [] = fail "Software bug detected: no input for parseTermStream!"
parseTermStream str = parseSingleTerm ([], str) >>= optimizeNesting
    where    optimizeNesting (tt@((t, p):trs), ts) = return (optimizeList (L tt, p), ts)
        parseSingleTerm (trms, [])                   = return (trms, [])
        parseSingleTerm (trms, ((Natural n, p):ts))  = return (trms ++ [(N n,p)], ts) >>= parseSingleTerm
        parseSingleTerm (trms, ((Variable n, p):ts)) = return (trms ++ [(V n,p)], ts) >>= parseSingleTerm
        parseSingleTerm (trms, ((PToken, p):ts))     = return (trms ++ [(P,p)], ts)   >>= parseSingleTerm
        parseSingleTerm (trms, ((TToken, p):ts))     = return (trms ++ [(T,p)], ts)   >>= parseSingleTerm
        parseSingleTerm (trms, ((SToken, p):ts))     = return (trms ++ [(S,p)], ts)   >>= parseSingleTerm
        parseSingleTerm (trms, ((Equal, p):ts))      = fail ((showPosition p) ++ " - Unexpected '=', probably ',' is missing.")
        parseSingleTerm (trms, ((HClose, p):ts)) = return (trms, ts)
        parseSingleTerm (trms, ((HOpen, p):ts)) = parseSingleTerm ([], ts) >>= addTermList >>= parseSingleTerm
            where    addTermList (trs, rest) = return (trms ++ [(L trs, p)], rest)

        --parseSingleTerm (trms, ((t,p):ts)) = fail ((showPosition p) ++ " - Software bug detected. Unexpected token: " ++ (show t))

        optimizeList ((L [x]), q)                = (optimizeList x)
        optimizeList ((L xs ), q)                = ((L (map optimizeList xs)), q)
        optimizeList (x, q)                      = (x, q)




------------------------------------ FEM Runtime --------------------------------------------
-- Evaluate program. The result will be an number or error message.
eval :: FemProg -> Result FemGetal
eval (decl, term) = evaluableTerm (decl, []) term >>= execTerm decl >>= evalTerm
    where    evalTerm (NR n) = return n
        evalTerm (FR n p a ac f) = fail ("Can't execute partially applied function: " ++ n ++ ", expecting arguments: " ++ (concat $intersperse ", " ac))
        evalTerm d = fail "Software bug detected: not number nor partial function in evalTerm!"


-- Convert an expression to evaluable object (lazy evaluation).
evaluableTerm::ContextLookup->FemParsedTerm->Result FemRuntimeTerm
evaluableTerm _  (S,p)               = return (FR "S" p [] ["arg"] (Right femNative_S))    -- introduce natives
evaluableTerm _  (P,p)               = return (FR "P" p [] ["arg"] (Right femNative_P))
evaluableTerm _  (T,p)               = return (FR "T" p [] ["arg","true","false"] (Right femNative_T))
evaluableTerm _  ((N n), p)          = return (NR n)        -- introduce primitive constant
evaluableTerm _  ((L []), p)         = fail ((showPosition p) ++ " - Empty group ()")
evaluableTerm (f,a) ((L (t:trms)),p) = return (LR p a (t:trms))
evaluableTerm (f,a) ((V name), p)    =
    case (lookup name a) of
        Just term    -> return term                            -- inject partially applied function
        Nothing    -> case lookup name f of
            (Just (pf, arg, term))    -> return (FR name pf [] arg (Left term))    -- introduce top level function
            Nothing            -> fail ((showPosition p) ++ " - Variable not found: " ++ name)


{- Execute delayed/lazy term
 - Function will terminate with either primitive value or partially applied FEM variable. -}
execTerm::[FemDeclaratie]->FemRuntimeTerm->Result FemRuntimeTerm
execTerm _ d@(NR n) = return d                    -- primitive value
execTerm flc (LR p alc (lamfunc:args)) = evaluableTerm (flc,alc) lamfunc >>= evaluableLambda args >>= execTerm flc
    where    evaluableLambda [] r = return r    -- L [ single term ] => single term
        evaluableLambda (t:ts) (NR n) = fail ((showPosition p) ++ " - Variable with at least one parameter expected, but got primitive: " ++ (show n))
        evaluableLambda (t:ts) (FR nm p arg (a:ags) f) = evaluableTerm (flc,alc) t >>= addArgument >>= evaluableLambda ts    -- fill in arguments. for partial application the function runs in own context, while arguments are from current context
            where    addArgument rt = return (FR nm p ((a,rt):arg) ags f)
        evaluableLambda (t:ts) trm = execTerm flc trm >>= evaluableLambda (t:ts)    -- full FR or L term, evaluate and apply the rest of arguments

execTerm _ d@(FR nm p ctx (a:as) func) = return d    -- partially applied function
execTerm flc (FR nm p ctx [] func) = applyFunc func    -- until either primitive val or partially applied func
    where    applyFunc (Left term) =
            case evaluableTerm (flc,ctx) term of
                Success term    -> execTerm flc term
                Error msg    -> Error (msg ++ "\n" ++ (showPosition p) ++ " - Failed to execute: " ++ nm)

        applyFunc (Right term) = 
            case term flc ctx of
                Error msg -> Error (msg ++ "\n       - Executed at: " ++ (showPosition p))
                Success val -> execTerm flc val    -- usually NR n






----------------------- Native bindings -------------------------
-- Increment
femNative_S :: FemSystemCallback
femNative_S lk [(a,t)] = execTerm lk t >>= updateWaarde
    where    updateWaarde (NR n) = return (NR (n+1))        -- direct execute
        updateWaarde d@(FR n p a ac f) = return (FR "<anonymous>" (-1,-1) [] ac (Right (femLambda [] (femNative_S lk) d)))

-- Decrement
femNative_P :: FemSystemCallback
femNative_P lk [(a,t)] = execTerm lk t >>= updateWaarde
    where    updateWaarde (NR 0) = return (NR 0)        -- direct execute
        updateWaarde (NR n) = return (NR (n-1))
        updateWaarde d@(FR n p a ac f) = return (FR "<anonymous>" (-1,-1) [] ac (Right (femLambda [] (femNative_P lk) d)))

-- Test (not)
femNative_T :: FemSystemCallback
femNative_T lk args = execTerm lk (takejust (lookup "arg" args)) >>= executeT -- executeT (takejust (lookup "arg" args)) -- 
    where    executeT (NR 0) = return taakTrue
        executeT (NR n) = return taakFalse
        executeT d@(FR n p a ac f) = return (FR "<anonymous>" (-1,-1) [] ac (Right (femLambda args (femNative_T lk) d)))
        taakTrue = takejust (lookup "true" args)
        taakFalse = takejust (lookup "false" args)
        takejust (Just n) = n


-- Native lambda, used to wrap native (black-box) functions
femLambda oargs nat (FR n p a ac f) lk args = (return (FR n p (a++args) [] f)) >>= execTerm lk >>= execNative >>= nat
    where    execNative rt = return ([("arg", rt)] ++ oargs)



Это простейший LL(1) парсер-интерпретатор + пара debug функций.  Коррктно выводит контекст ошибки (parsing, runtime) и т.п. отсюда много кода. Просто парсер укладывается в пару десятков строк. Кстати пример как реализуются не типичные для чистых декларативных языков последовательные операции посредством монад.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
mr.DUDA
Дата 21.1.2008, 22:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 4
Всего: 232



Какой кошмарный язык. Напомнило мне времена когда не зная ничего кроме бейсика пытался вникнуть в примеры из книги Страуструпа...


--------------------
user posted image
PM MAIL WWW   Вверх
Real
Дата 18.4.2008, 20:41 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 507
Регистрация: 9.11.2007

Репутация: -3
Всего: -1



Цитата(LSD @ 21.1.2008,  14:36)
Цитата(APM @  21.1.2008,  14:28 Найти цитируемый пост)
У кого какие мнения? =)

1. Есть специальный раздел для тестов.
2. Без исходников, конфигурации и версий компиляторов это не тест, а баловство.

Где этот раздел?
PM   Вверх
Ch0bits
Дата 18.4.2008, 21:23 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Python Dev.
****


Профиль
Группа: Завсегдатай
Сообщений: 2124
Регистрация: 21.2.2005
Где: Казань

Репутация: 1
Всего: 62



PM WWW   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила ведения Религиозных войн
Smartov
1. Уважайте собеседника
2. Собеседник != враг
3. Старайтесь воздерживаться от тем вида "Windows Rulez" или "Linux Rulez"

С уважением, Smartov.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Религиозные войны | Следующая тема »


 




[ Время генерации скрипта: 0.0519 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.