diff --git a/src/FibLib/Library.fs b/src/FibLib/Library.fs index e1f6c71..a7af3c6 100644 --- a/src/FibLib/Library.fs +++ b/src/FibLib/Library.fs @@ -8,7 +8,7 @@ open System.Collections.Generic module Utils = let smartDedent (input: string) = - let lines = input.Replace("\r\n", "\n").Split('\n') |> List.ofArray + let lines = input.Replace("\r\n", "\n").Split '\n' |> List.ofArray // 1. Hitta den minsta indenteringen bland alla rader som har text let minIndent = @@ -20,6 +20,7 @@ module Utils = | indents -> List.min indents // 2. Dra av exakt så många mellanslag från alla rader + // för att dessa inte ska vara med i det slutgiltiga dokumentet. let dedented = lines |> List.map (fun l -> @@ -29,30 +30,7 @@ module Utils = // 3. Slå ihop och städa bort överflödiga radbrytningar i början/slutet (String.concat "\n" dedented).Trim('\n', '\r') - -module Utils3 = - let smartDedent (input: string) = - let text = input.Trim('\n', '\r') - let lines = text.Replace("\r\n", "\n").Split('\n') |> List.ofArray - - let rec loop lines currentBase acc = - match lines with - | [] -> List.rev acc |> String.concat "\n" - | line :: tail -> - if System.String.IsNullOrWhiteSpace(line) then - loop tail currentBase ("" :: acc) - else - let indent = line.Length - line.TrimStart().Length - - if indent = 0 then - loop tail 0 (line :: acc) - else - let newBase = if currentBase = 0 then indent else currentBase - let toStrip = min newBase indent - let stripped = line.Substring toStrip - loop tail newBase (stripped :: acc) - - loop lines 0 [] + // ========================================== // 1. AST & Utils @@ -71,26 +49,10 @@ module Ast = type Document = BlockNode list - type TagRenderer = string list -> InlineNode list -> InlineNode + type TagRenderer = Map -> string list -> InlineNode list -> InlineNode -module Utils2 = - let smartDedent (input: string) = - let text = input.Trim('\n', '\r') - let lines = text.Replace("\r\n", "\n").Split '\n' |> List.ofArray - let rec loop lines currentBase acc = - match lines with - | [] -> List.rev acc |> String.concat "\n" - | line :: tail -> - if System.String.IsNullOrWhiteSpace line then - loop tail currentBase ("" :: acc) - else - let indent = line.Length - line.TrimStart().Length - let newBase = if currentBase = 0 then indent else currentBase - let toStrip = min newBase indent - loop tail newBase (line.Substring toStrip :: acc) - loop lines 0 [] // ========================================== // 2. Parser @@ -121,7 +83,7 @@ module Parser = pchar '{' >>. pRawBodyRef.Value .>> pchar '}' |>> sprintf "{%s}" ]) |>> String.concat "" - let pBody = + let pBody: Parser = between (pstring "{") (pstring "}") pRawBodyRef.Value >>= fun raw -> match run (many pInline .>> eof) (Utils.smartDedent raw) with | Success(n, _, _) -> preturn n @@ -214,112 +176,6 @@ module Parser = | Failure(e, _, _) -> failwith e -module Parser2 = - open Ast - - let pInline, pInlineRef = createParserForwardedToRef() - - // --- De saknade hjälpfunktionerna --- - let getSectionLevel (name: string) = - if name = "section" then 1 - elif name = "subsection" then 2 - elif name.StartsWith("sub") && name.EndsWith "section" then (name.Length - 7) / 3 + 1 - else 1 - - let isSection (name: string) = name.EndsWith "section" - - let pNewline = newline - - let pArg = spaces >>. manyChars (noneOf ",]") .>> spaces - let pArgs = between (pstring "[") (pstring "]") (sepBy pArg (pstring ",")) - // ------------------------------------ - - let pRawBody, pRawBodyRef = createParserForwardedToRef() - pRawBodyRef.Value <- - many (choice [ - many1Chars (noneOf "{}") - pchar '{' >>. pRawBodyRef.Value .>> pchar '}' |>> sprintf "{%s}" - ]) |>> String.concat "" - - let pBody = - between (pstring "{") (pstring "}") pRawBodyRef.Value >>= fun raw -> - match run (many pInline .>> eof) (Utils.smartDedent raw) with - | Success(n, _, _) -> preturn n - | Failure(m, _, _) -> fail m - - let pMultilineCode = - pstring "@\"\"\"" >>. manyCharsTill anyChar (pstring "\"\"\"") - |>> fun c -> Expr(Utils.smartDedent c, None) - - let pParenBody, pParenBodyRef = createParserForwardedToRef() - pParenBodyRef.Value <- - many (choice [ - many1Chars (noneOf "()") - pchar '(' >>. pParenBodyRef.Value .>> pchar ')' |>> sprintf "(%s)" - ]) |>> String.concat "" - - // let pExpr2 = - // attempt (pstring "@(") >>. manyChars (noneOf ")") .>> pstring ")" - // |>> fun c -> Expr(c, None) - - let pExpr = - attempt (pstring "@(") >>. pParenBodyRef.Value .>> pstring ")" - |>> fun c -> Expr(c, None) - - let pInlineCommand = - attempt ( - pchar '@' >>. many1Chars asciiLetter >>= fun name -> - if isSection name then fail "Sektioner är block-element." - else preturn name - ) - .>>. opt pArgs - .>>. opt pBody - |>> fun ((n, a), b) -> Element(n, defaultArg a [], defaultArg b []) - - pInlineRef.Value <- choice [ - pMultilineCode - pExpr - pInlineCommand - many1Chars (noneOf "@{}\n\r") |>> Text - attempt (pNewline .>> notFollowedBy pNewline) |>> fun _ -> Text "\n" - ] - - let pSectionBlock = - attempt ( - pchar '@' >>. many1Chars asciiLetter >>= fun name -> - if isSection name then preturn name - else fail "Inte en sektion." - ) - .>>. opt pArgs - .>>. opt pBody - |>> fun ((name, argsOpt), bodyOpt) -> - Section(getSectionLevel name, defaultArg argsOpt [], defaultArg bodyOpt []) - - let pParagraphBlock = many1 pInline |>> Paragraph - - let pBlock = choice [ pSectionBlock; pParagraphBlock ] - - let pDocument = - spaces - >>. opt ( - pstring "---" - // HÄR ÄR FIXEN: attempt runt pNewline och pstring - >>. manyCharsTill anyChar (attempt (pNewline >>. pstring "---")) - |>> fun yamlStr -> - let deserializer = DeserializerBuilder().Build() - let dict = deserializer.Deserialize>(yamlStr) - if isNull dict then Map.empty - else dict |> Seq.map (fun kvp -> kvp.Key, kvp.Value) |> Map.ofSeq - ) - .>> spaces - .>>. sepEndBy pBlock (many1 pNewline) - |>> fun (headerOpt, blocks) -> - (defaultArg headerOpt Map.empty, blocks) - - let parse i = - match run pDocument i with - | Success(r, _, _) -> r - | Failure(e, _, _) -> failwith e // ========================================== // 3. Execution & Printer // ========================================== @@ -329,11 +185,13 @@ type IEvaluator = module Execution = open Ast - let rec transform (prelude: Map) (eval: IEvaluator) = - function - | Element(n, a, c) when prelude.ContainsKey n -> prelude.[n] a (c |> List.map (transform prelude eval)) - | Element(n, a, c) -> Element(n, a, c |> List.map (transform prelude eval)) - | Expr(c, _) -> RawHtml(eval.Evaluate c) + let rec transform (metadata: Map) (prelude: Map) (eval: IEvaluator) = function + | Element(n, a, c) when prelude.ContainsKey n -> + // Skicka in metadata som första argument till din funktion + prelude.[n] metadata a (c |> List.map (transform metadata prelude eval)) + | Element(n, a, c) -> + Element(n, a, c |> List.map (transform metadata prelude eval)) + | Expr(c, _) -> RawHtml (eval.Evaluate c) | n -> n module HtmlPrinter = @@ -427,49 +285,4 @@ module Evaluators = Kritiskt FSI-systemfel: %s """ ex.Message - // interface IEvaluator with - // member _.Evaluate(code: string) = - // sbOut.Clear() |> ignore - // sbErr.Clear() |> ignore - - // try - // printfn "%s" code - - // // Kör koden i FSI - // let result, _warnings = session.EvalInteractionNonThrowing code - - // // Läs av vad kompilatorn spottade ut - // let output = sbOut.ToString().Trim() - // let errors = sbErr.ToString().Trim() - - // match result with - // | Choice1Of2 (Some fsiValue) -> - // // Det gick bra och koden returnerade ett värde - // let valStr = sprintf "%A" fsiValue.ReflectionValue - // if System.String.IsNullOrEmpty(output) then valStr - // else output + "\n" + valStr - - // | Choice1Of2 None -> - // // Det gick bra, men koden returnerade inget värde (t.ex. en let-bindning) - // // Om det fanns utskrifter i sbErr (t.ex. varningar), kan vi visa dem här: - // if not (System.String.IsNullOrEmpty(errors)) then - // output + sprintf "\n %A" errors - // else - // output - - // | Choice2Of2 ex -> - // // FSI kastade ett exception (t.ex. syntaxfel eller runtime-fel) - // let fsiErrorOutput = if System.String.IsNullOrEmpty(errors) then "Ingen ytterligare FSI-output." else errors - // sprintf """ - //
- // FSI Exekveringsfel!
- // Kod som kördes: %s

- // Exception: %s
- // FSI Stderr:
%s
- //
""" code ex.Message fsiErrorOutput - - // with ex -> - // // Fångar upp om själva anropet till session.EvalInteractionNonThrowing kraschar helt - // sprintf """
- // Kritiskt FSI-systemfel: %s - //
""" ex.Message + diff --git a/src/Fibble/Program.fs b/src/Fibble/Program.fs index 3824277..f92e40c 100644 --- a/src/Fibble/Program.fs +++ b/src/Fibble/Program.fs @@ -7,41 +7,36 @@ open Fibble.FibLib.Ast // Ger oss tillgång till Element, Text, RawHtml etc. // ========================================== // makeElem returnerar nu en strukturerad Element-nod istället för en sträng -let makeElem (name : string) = - (name, fun args children -> Element(name, args, children)) +let elem (name : string) = + (name, fun meta args children -> Element(name, args, children)) let makeElems lst = - List.map makeElem lst + List.map elem lst -let defaultPrelude : Map = - [ "b"; "em"; "i"; "strong" ] - |> makeElems - |> Map.ofList -let myPrelude : Map = - Map.ofList [ - "quotient", fun args _ -> +let myPrelude : Map = + Map [ + "quotient", fun _ args _ -> if args.Length >= 2 then Text (sprintf "%d" (int args.[0] / int args.[1])) else Text "[Fel: quotient kräver två argument]" - - "bold", fun args children -> + "bold", fun _ args children -> Element("b", args, children) - - "link", fun args children -> + "value", fun meta args _ -> + Text meta[args.Head] + "link", fun _ args children -> let url = if args.Length > 0 then args.[0].Trim('"') else "#" - // Vi mappar argumenten till HTML-attribut (t.ex. href="...") Element("a", [sprintf "href=\"%s\"" url], children) - + // @br har varken argument eller barn, så vi returnerar bara rå HTML direkt - "br", fun _ _ -> - RawHtml "
" - ] + "br", fun _ _ _ -> RawHtml "
" + ] // ========================================== // 2. Mall och Evaluator // ========================================== + let pageTemplate = """ @@ -119,7 +114,7 @@ let increment () = Första anropet: @(increment()) Andra anropet: @(increment()) -Test av utskrift: +Test av utskrift: @value[date] " // ========================================== // 3. Huvudpipeline @@ -128,7 +123,7 @@ let processDocument (source: string) = let evaluator = Evaluators.FsiEvaluator() // Steg 1: Parsa koden - let (metadata, rawBlocks) = Parser.parse source + let metadata, rawBlocks = Parser.parse source // Hjälpfunktion: Gå igenom trädet och byt ut @value{...} mot faktiskt data från YAML let rec resolveMeta = function @@ -146,11 +141,11 @@ let processDocument (source: string) = | Paragraph children -> Paragraph (children |> List.map resolveMeta - |> List.map (Execution.transform myPrelude evaluator)) + |> List.map (Execution.transform metadata myPrelude evaluator)) | Section(l, a, children) -> Section(l, a, children |> List.map resolveMeta - |> List.map (Execution.transform myPrelude evaluator)) + |> List.map (Execution.transform metadata myPrelude evaluator)) ) // Steg 3: Be printern skriva ut trädet till HTML