├── .editorconfig ├── .gitignore ├── .travis.yml ├── License.md ├── Naggum.Assembler ├── App.config ├── Assembler.fs ├── AssemblyInfo.fs ├── Naggum.Assembler.fsproj ├── Processor.fs ├── Program.fs ├── Representation.fs └── packages.config ├── Naggum.Backend ├── AssemblyInfo.fs ├── Matchers.fs ├── MaybeMonad.fs ├── Naggum.Backend.fsproj ├── Reader.fs └── packages.config ├── Naggum.Compiler ├── App.config ├── AssemblyInfo.fs ├── ClrGenerator.fs ├── Context.fs ├── FormGenerator.fs ├── Generator.fs ├── GeneratorFactory.fs ├── Globals.fs ├── IGenerator.fs ├── MathGenerator.fs ├── Naggum.Compiler.fsproj ├── NumberGen.fs ├── Program.fs ├── StringGen.fs └── packages.config ├── Naggum.Interactive ├── Naggum.Interactive.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── app.config ├── Naggum.Runtime ├── Cons.cs ├── Naggum.Runtime.csproj ├── Properties │ └── AssemblyInfo.cs ├── Reader.cs └── Symbol.cs ├── Naggum.Test ├── AssemblerTests.fs ├── AssemblyInfo.fs ├── CompilerTest.fs ├── InstructionTests.fs ├── MatchersTests.fs ├── Naggum.Test.dll.config ├── Naggum.Test.fsproj ├── Process.fs ├── ProcessorTests.fs └── packages.config ├── Naggum.sln ├── Readme.md ├── appveyor.yml ├── default.nix ├── docs ├── Makefile ├── about.rst ├── build-guide.rst ├── conf.py ├── index.rst ├── make.bat ├── specification.rst └── usage.rst └── tests ├── comment.naggum ├── comment.result ├── let-funcall.naggum ├── let-funcall.result ├── test.naggum └── test.result /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /.vs/ 3 | /docs/_build/ 4 | /packages/ 5 | 6 | /*.user 7 | 8 | bin/ 9 | obj/ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | mono: 3 | - 4.4.2 4 | - latest 5 | solution: Naggum.sln 6 | install: 7 | - nuget restore Naggum.sln 8 | - nuget install xunit.runner.console -Version 2.1.0 -OutputDirectory testrunner 9 | script: 10 | - xbuild /p:Configuration=Release /p:TargetFrameworkVersion="v4.5" Naggum.sln 11 | - mono ./testrunner/xunit.runner.console.2.1.0/tools/xunit.console.exe ./Naggum.Test/bin/Release/Naggum.Test.dll 12 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (C) 2011–2016 by Naggum authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Naggum.Assembler/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Naggum.Assembler/Assembler.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Assembler.Assembler 2 | 3 | open System 4 | open System.Reflection 5 | open System.Reflection.Emit 6 | 7 | open Naggum.Assembler.Representation 8 | 9 | let private getMethodAttributes (m : MethodDefinition) = 10 | let empty = enum 0 11 | let conditions = 12 | [ (m.Visibility = Public, MethodAttributes.Public) 13 | (true, MethodAttributes.Static) ] // TODO: Proper static method detection 14 | 15 | conditions 16 | |> List.map (fun (c, r) -> if c then r else empty) 17 | |> List.fold (|||) empty 18 | 19 | let private findMethod (signature : MethodSignature) = 20 | let ``type`` = signature.ContainingType.Value 21 | ``type``.GetMethod (signature.Name, Array.ofList signature.ArgumentTypes) 22 | 23 | let private buildMethodBody (m : MethodDefinition) (builder : MethodBuilder) = 24 | use generator = new GrEmit.GroboIL (builder) 25 | 26 | m.Body 27 | |> List.iter (function 28 | | Call signature -> 29 | let methodInfo = findMethod signature 30 | generator.Call methodInfo 31 | | LdcI4 i -> generator.Ldc_I4 i 32 | | Ldstr string -> generator.Ldstr string 33 | | Simple Add -> generator.Add () 34 | | Simple Div -> generator.Div (false) // TODO: Signed division support 35 | | Simple Mul -> generator.Mul () 36 | | Simple Ret -> generator.Ret () 37 | | Simple Sub -> generator.Sub ()) 38 | 39 | let private assembleUnit (assemblyBuilder : AssemblyBuilder) (builder : ModuleBuilder) = function 40 | | Method m -> 41 | let name = m.Name 42 | let attributes = getMethodAttributes m 43 | let returnType = m.ReturnType 44 | let argumentTypes = Array.ofList m.ArgumentTypes 45 | let methodBuilder = builder.DefineGlobalMethod (name, 46 | attributes, 47 | returnType, 48 | argumentTypes) 49 | if Set.contains EntryPoint m.Metadata then 50 | assemblyBuilder.SetEntryPoint methodBuilder 51 | buildMethodBody m methodBuilder 52 | 53 | /// Assembles the intermediate program representation. Returns an assembled 54 | /// module. 55 | let assemble (mode : AssemblyBuilderAccess) (assembly : Assembly) = 56 | let name = AssemblyName assembly.Name 57 | let domain = AppDomain.CurrentDomain 58 | let builder = domain.DefineDynamicAssembly (name, mode) 59 | let fileName = assembly.Name + ".exe" // TODO: Proper file naming 60 | let moduleBuilder = builder.DefineDynamicModule (assembly.Name, fileName) 61 | assembly.Units |> List.iter (assembleUnit builder moduleBuilder) 62 | moduleBuilder.CreateGlobalFunctions () 63 | builder 64 | 65 | /// Assembles the intermediate program representation. Returns a list of 66 | /// assemblies ready for saving. 67 | let assembleAll (mode : AssemblyBuilderAccess) 68 | (assemblies : Assembly seq) : AssemblyBuilder seq = 69 | assemblies 70 | |> Seq.map (assemble mode) 71 | -------------------------------------------------------------------------------- /Naggum.Assembler/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Naggum.Assembler.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.InteropServices 5 | 6 | [] 7 | [] 8 | [] 9 | [] 10 | [] 11 | [] 12 | 13 | () 14 | -------------------------------------------------------------------------------- /Naggum.Assembler/Naggum.Assembler.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 40b84f1e-1823-4255-80d4-1297613025c1 9 | Exe 10 | Naggum.Assembler 11 | Naggum.Assembler 12 | v4.6.1 13 | true 14 | 4.4.0.0 15 | Naggum.Assembler 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | 3 26 | AnyCPU 27 | bin\Debug\Naggum.Assembler.XML 28 | true 29 | 30 | 31 | pdbonly 32 | true 33 | true 34 | bin\Release\ 35 | TRACE 36 | 3 37 | AnyCPU 38 | bin\Release\Naggum.Assembler.XML 39 | true 40 | 41 | 42 | 11 43 | 44 | 45 | 46 | 47 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 48 | 49 | 50 | 51 | 52 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | ..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll 69 | True 70 | 71 | 72 | ..\packages\GrEmit.2.1.7\lib\net40\GrEmit.dll 73 | True 74 | 75 | 76 | 77 | 78 | 79 | 80 | Naggum.Backend 81 | {243738f3-d798-4b09-8797-f90b21414b60} 82 | True 83 | 84 | 85 | 92 | -------------------------------------------------------------------------------- /Naggum.Assembler/Processor.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Assembler.Processor 2 | 3 | open System 4 | open System.IO 5 | open System.Reflection 6 | 7 | open Naggum.Assembler.Representation 8 | open Naggum.Backend 9 | open Naggum.Backend.Matchers 10 | 11 | let private (|SimpleOpCode|_|) = function 12 | | Symbol "add" -> Some (Simple Add) 13 | | Symbol "div" -> Some (Simple Div) 14 | | Symbol "mul" -> Some (Simple Mul) 15 | | Symbol "ret" -> Some (Simple Ret) 16 | | Symbol "sub" -> Some (Simple Sub) 17 | | _ -> None 18 | 19 | let private processMetadataItem = function 20 | | Symbol ".entrypoint" -> EntryPoint 21 | | other -> failwithf "Unrecognized metadata item definition: %A" other 22 | 23 | let private resolveAssembly _ = 24 | Assembly.GetAssembly(typeof) // TODO: Assembly resolver 25 | 26 | let private resolveType name = 27 | let result = Type.GetType name // TODO: Resolve types from the assembler context 28 | if isNull result then 29 | failwithf "Type %s could not be found" name 30 | 31 | result 32 | 33 | let private resolveTypes = 34 | List.map (function 35 | | Symbol name -> resolveType name 36 | | other -> failwithf "Unrecognized type: %A" other) 37 | 38 | let private processMethodSignature = function 39 | | [Symbol assembly 40 | Symbol typeName 41 | Symbol methodName 42 | List argumentTypes 43 | Symbol returnType] -> 44 | { Assembly = Some (resolveAssembly assembly) // TODO: Resolve types from current assembly 45 | ContainingType = Some (resolveType typeName) // TODO: Resolve methods without a type (e.g. assembly methods) 46 | Name = methodName 47 | ArgumentTypes = resolveTypes argumentTypes 48 | ReturnType = resolveType returnType } 49 | | other -> failwithf "Unrecognized method signature: %A" other 50 | 51 | let private processInstruction = function 52 | | List [Symbol "ldstr"; String s] -> Ldstr s 53 | | List [Symbol "ldc.i4"; Integer i] -> LdcI4 i 54 | | List [Symbol "call"; List calleeSignature] -> 55 | let signature = processMethodSignature calleeSignature 56 | Call signature 57 | | List [SimpleOpCode r] -> r 58 | | other -> failwithf "Unrecognized instruction: %A" other 59 | 60 | let private addMetadata metadata method' = 61 | List.fold (fun ``method`` metadataExpr -> 62 | let metadataItem = processMetadataItem metadataExpr 63 | { ``method`` with Metadata = Set.add metadataItem ``method``.Metadata }) 64 | method' 65 | metadata 66 | 67 | let private addBody body method' = 68 | List.fold (fun ``method`` bodyClause -> 69 | let instruction = processInstruction bodyClause 70 | { ``method`` with Body = List.append ``method``.Body [instruction] }) 71 | method' 72 | body 73 | 74 | let private processAssemblyUnit = function 75 | | List (Symbol ".method" 76 | :: Symbol name 77 | :: List argumentTypes 78 | :: Symbol returnType 79 | :: List metadata 80 | :: body) -> 81 | let definition = 82 | { Metadata = Set.empty 83 | Visibility = Public // TODO: Determine method visibility 84 | Name = name 85 | ArgumentTypes = resolveTypes argumentTypes 86 | ReturnType = resolveType returnType 87 | Body = List.empty } 88 | definition 89 | |> addMetadata metadata 90 | |> addBody body 91 | |> Method 92 | | other -> failwithf "Unrecognized assembly unit definition: %A" other 93 | 94 | let private prepareTopLevel = function 95 | | List (Symbol ".assembly" :: Symbol name :: units) -> 96 | { Name = name 97 | Units = List.map processAssemblyUnit units } 98 | | other -> failwithf "Unknown top-level construct: %A" other 99 | 100 | /// Prepares the source file for assembling. Returns the intermediate 101 | /// representation of the source code. 102 | let prepare (fileName : string) (stream : Stream) : Assembly seq = 103 | let forms = Reader.parse fileName stream 104 | forms |> Seq.map prepareTopLevel 105 | -------------------------------------------------------------------------------- /Naggum.Assembler/Program.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Assembler.Program 2 | 3 | open System 4 | open System.IO 5 | open System.Reflection 6 | open System.Reflection.Emit 7 | 8 | type private ReturnCode = 9 | | Success = 0 10 | | Error = 1 11 | | InvalidArguments = 2 12 | 13 | let private printUsage () = 14 | let version = Assembly.GetExecutingAssembly().GetName().Version 15 | printfn "Naggum Assembler %A" version 16 | printfn "Usage: Naggum.Assembler [one or more file names]" 17 | 18 | let private printError (error : Exception) = 19 | printfn "Error: %s" (error.ToString ()) 20 | 21 | let private save (assembly : AssemblyBuilder) = 22 | let name = assembly.GetName().Name + ".exe" // TODO: See #45. ~ F 23 | assembly.Save name 24 | printfn "Assembly %s saved" name 25 | 26 | let private assemble fileName = 27 | use stream = File.OpenRead fileName 28 | let repr = Processor.prepare fileName stream 29 | let assemblies = Assembler.assembleAll AssemblyBuilderAccess.Save repr 30 | assemblies |> Seq.iter save 31 | 32 | let private nga = 33 | function 34 | | [| "--help" |] -> 35 | printUsage () 36 | ReturnCode.Success 37 | | fileNames when fileNames.Length > 0 -> 38 | try 39 | fileNames |> Array.iter assemble 40 | ReturnCode.Success 41 | with 42 | | error -> 43 | printError error 44 | ReturnCode.Error 45 | | _ -> 46 | printUsage () 47 | ReturnCode.InvalidArguments 48 | 49 | [] 50 | let main args = 51 | let result = nga args 52 | int result 53 | -------------------------------------------------------------------------------- /Naggum.Assembler/Representation.fs: -------------------------------------------------------------------------------- 1 | namespace Naggum.Assembler.Representation 2 | 3 | open System.Reflection 4 | 5 | type MetadataItem = 6 | | EntryPoint 7 | 8 | type Visibility = 9 | | Public 10 | 11 | type Type = System.Type 12 | 13 | type MethodSignature = 14 | { Assembly : Assembly option 15 | ContainingType : Type option 16 | Name : string 17 | ArgumentTypes : Type list 18 | ReturnType : Type } 19 | 20 | type SimpleInstruction = 21 | | Add 22 | | Div 23 | | Mul 24 | | Ret 25 | | Sub 26 | 27 | type Instruction = 28 | | Call of MethodSignature 29 | | Ldstr of string 30 | | LdcI4 of int 31 | | Simple of SimpleInstruction 32 | 33 | type MethodDefinition = 34 | { Metadata : Set 35 | Visibility : Visibility 36 | Name : string 37 | ArgumentTypes : Type list 38 | ReturnType : Type 39 | Body : Instruction list } 40 | 41 | type AssemblyUnit = 42 | | Method of MethodDefinition 43 | 44 | type Assembly = 45 | { Name : string 46 | Units : AssemblyUnit list } 47 | override this.ToString () = sprintf "%A" this 48 | -------------------------------------------------------------------------------- /Naggum.Assembler/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Naggum.Backend/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Naggum.Backend.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.InteropServices 5 | 6 | [] 7 | [] 8 | [] 9 | [] 10 | [] 11 | [] 12 | 13 | () 14 | -------------------------------------------------------------------------------- /Naggum.Backend/Matchers.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Backend.Matchers 2 | 3 | let (|Symbol|Object|List|) = function 4 | | Reader.Atom (Reader.Object o) -> Object o 5 | | Reader.Atom (Reader.Symbol x) -> Symbol x 6 | | Reader.List l -> List l 7 | 8 | let (|Integer|_|) = function 9 | | Object (:? int as i) -> Some i 10 | | _ -> None 11 | 12 | let (|String|_|) = function 13 | | Object (:? string as s) -> Some s 14 | | _ -> None 15 | -------------------------------------------------------------------------------- /Naggum.Backend/MaybeMonad.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Backend.MaybeMonad 2 | 3 | type MaybeMonad() = 4 | 5 | member __.Bind (m, f) = 6 | match m with 7 | | Some v -> f v 8 | | None -> None 9 | 10 | member __.Return v = Some v 11 | 12 | let maybe = MaybeMonad () 13 | -------------------------------------------------------------------------------- /Naggum.Backend/Naggum.Backend.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 2.0 8 | 243738f3-d798-4b09-8797-f90b21414b60 9 | Library 10 | Naggum.Backend 11 | Naggum.Backend 12 | v4.6 13 | 4.4.0.0 14 | true 15 | Naggum.Backend 16 | 17 | 18 | true 19 | full 20 | false 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | 3 25 | bin\Debug\Naggum.Backend.XML 26 | 27 | 28 | pdbonly 29 | true 30 | true 31 | bin\Release\ 32 | TRACE 33 | 3 34 | bin\Release\Naggum.Backend.XML 35 | 36 | 37 | 11 38 | 39 | 40 | 41 | 42 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 43 | 44 | 45 | 46 | 47 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ..\packages\FParsec.1.0.2\lib\net40-client\FParsec.dll 62 | True 63 | 64 | 65 | ..\packages\FParsec.1.0.2\lib\net40-client\FParsecCS.dll 66 | True 67 | 68 | 69 | ..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll 70 | True 71 | 72 | 73 | 74 | 75 | 76 | 77 | 84 | -------------------------------------------------------------------------------- /Naggum.Backend/Reader.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Backend.Reader 2 | 3 | open System 4 | open System.IO 5 | open FParsec 6 | 7 | type Value = 8 | | Object of obj 9 | | Symbol of string 10 | 11 | type SExp = 12 | | Atom of Value 13 | | List of SExp list 14 | 15 | let comment = (skipChar ';') .>> (skipRestOfLine true) 16 | let skipSpaceAndComments = skipMany (spaces1 <|> comment) 17 | let commented parser = skipSpaceAndComments >>. parser .>> skipSpaceAndComments 18 | let list, listRef = createParserForwardedToRef() 19 | let numberOptions = 20 | NumberLiteralOptions.AllowMinusSign ||| NumberLiteralOptions.AllowExponent ||| NumberLiteralOptions.AllowHexadecimal 21 | ||| NumberLiteralOptions.AllowFraction ||| NumberLiteralOptions.AllowSuffix 22 | 23 | let pnumber : Parser = 24 | let pliteral = numberLiteral numberOptions "number" 25 | fun stream -> 26 | let reply = pliteral stream 27 | if reply.Status = Ok then 28 | let result : NumberLiteral = reply.Result 29 | if result.IsInteger then 30 | if result.SuffixLength = 1 && result.SuffixChar1 = 'L' then 31 | Reply((int64 result.String) :> obj |> Object) 32 | else if not (result.SuffixLength = 1) then Reply((int32 result.String) :> obj |> Object) 33 | else Reply(ReplyStatus.Error, messageError <| sprintf "Unknown suffix: %A" result.SuffixChar1) 34 | else if result.SuffixLength = 1 && result.SuffixChar1 = 'f' then 35 | Reply((float result.String) :> obj |> Object) 36 | else if not (result.SuffixLength = 1) then Reply((single result.String) :> obj |> Object) 37 | else Reply(ReplyStatus.Error, messageError <| sprintf "Unknown suffix: %A" result.SuffixChar1) 38 | else Reply(reply.Status, reply.Error) 39 | 40 | let string = 41 | let normalChar = satisfy (fun c -> c <> '\"') 42 | between (pstring "\"") (pstring "\"") (manyChars normalChar) |>> (fun str -> str :> obj) |>> Object 43 | 44 | let symChars = (anyOf "+-*/=<>!?._") //chars that are valid in the symbol name 45 | let symbol = (many1Chars (letter <|> digit <|> symChars)) |>> Symbol 46 | let atom = (pnumber <|> string <|> symbol) |>> Atom 47 | let listElement = choice [ atom; list ] 48 | let sexp = commented (pchar '(') >>. many (commented listElement) .>> commented (pchar ')') |>> List 49 | let parser = many1 (choice [ atom; sexp ]) 50 | 51 | do listRef := sexp 52 | 53 | let parse (sourceName : string) (source : Stream) = 54 | let form = runParserOnStream parser () sourceName source Text.Encoding.UTF8 55 | match form with 56 | | Success(result, _, _) -> result 57 | | Failure(errorMsg, _, _) -> failwithf "Failure: %s" errorMsg 58 | -------------------------------------------------------------------------------- /Naggum.Backend/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Naggum.Compiler/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Naggum.Compiler/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Naggum.Compiler.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.InteropServices 5 | 6 | [] 7 | [] 8 | [] 9 | [] 10 | [] 11 | [] 12 | 13 | () 14 | -------------------------------------------------------------------------------- /Naggum.Compiler/ClrGenerator.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.ClrGenerator 2 | 3 | open System 4 | 5 | open Naggum.Backend.MaybeMonad 6 | open Naggum.Backend.Reader 7 | open Naggum.Compiler.Context 8 | open Naggum.Compiler.IGenerator 9 | 10 | let nearestOverload (clrType : Type) methodName types = 11 | let rec distanceBetweenTypes (derivedType : Type, baseType) = 12 | match derivedType with 13 | | null -> None 14 | | someType 15 | when someType = baseType -> Some 0 16 | | _ -> 17 | maybe { 18 | let! distance = distanceBetweenTypes (derivedType.BaseType, baseType) 19 | return distance + 1 20 | } 21 | let distance (availableTypes : Type list) (methodTypes : Type list) = 22 | if availableTypes.Length <> methodTypes.Length then 23 | None 24 | else 25 | Seq.zip availableTypes methodTypes 26 | |> Seq.map distanceBetweenTypes 27 | |> Seq.fold (fun state option -> 28 | maybe { 29 | let! stateNum = state 30 | let! optionNum = option 31 | return stateNum + optionNum 32 | }) (Some 0) 33 | let methods = clrType.GetMethods() |> Seq.filter (fun clrMethod -> clrMethod.Name = methodName) 34 | let methodsAndDistances = methods 35 | |> Seq.map (fun clrMethod -> clrMethod, 36 | distance types (clrMethod.GetParameters() 37 | |> Array.map (fun parameter -> 38 | parameter.ParameterType) 39 | |> Array.toList)) 40 | |> Seq.filter (snd >> Option.isSome) 41 | |> Seq.map (fun (clrMethod, distance) -> clrMethod, Option.get distance) 42 | |> Seq.toList 43 | if methodsAndDistances.IsEmpty then 44 | None 45 | else 46 | let minDistance = methodsAndDistances |> List.minBy snd |> snd 47 | let methods = methodsAndDistances |> List.filter (snd >> (fun d -> d = minDistance)) 48 | |> List.map fst 49 | if methods.IsEmpty then 50 | None 51 | else 52 | Some (List.head methods) 53 | 54 | type ClrCallGenerator (context : Context, 55 | clrType : Type, 56 | methodName : string, 57 | arguments : SExp list, 58 | gf : IGeneratorFactory) = 59 | let args = gf.MakeSequence context arguments 60 | let arg_types = args.ReturnTypes() 61 | let clrMethod = nearestOverload clrType methodName arg_types 62 | interface IGenerator with 63 | member __.Generate il = 64 | args.Generate il 65 | il.Call (Option.get clrMethod) 66 | 67 | member this.ReturnTypes() = 68 | [(Option.get clrMethod).ReturnType] 69 | 70 | type InstanceCallGenerator (context : Context, 71 | instance : SExp, 72 | methodName : string, 73 | arguments : SExp list, 74 | gf : IGeneratorFactory) = 75 | interface IGenerator with 76 | member __.Generate il = 77 | let instGen = gf.MakeGenerator context instance 78 | let argsGen = gf.MakeSequence context arguments 79 | let methodInfo = nearestOverload (instGen.ReturnTypes() |> List.head) methodName (argsGen.ReturnTypes()) 80 | if Option.isSome methodInfo then 81 | instGen.Generate il 82 | argsGen.Generate il 83 | il.Call (Option.get methodInfo) 84 | else failwithf "No overload found for method %A with types %A" methodName (argsGen.ReturnTypes()) 85 | 86 | member this.ReturnTypes () = 87 | let inst_gen = gf.MakeGenerator context instance 88 | let args_gen = gf.MakeSequence context arguments 89 | let methodInfo = nearestOverload (inst_gen.ReturnTypes() |> List.head) methodName (args_gen.ReturnTypes()) 90 | if Option.isSome methodInfo then 91 | [(Option.get methodInfo).ReturnType] 92 | else failwithf "No overload found for method %A with types %A" methodName (args_gen.ReturnTypes()) 93 | -------------------------------------------------------------------------------- /Naggum.Compiler/Context.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.Context 2 | 3 | open System 4 | open System.Collections.Generic 5 | open System.Reflection 6 | open System.Reflection.Emit 7 | 8 | open GrEmit 9 | 10 | open Naggum.Runtime 11 | 12 | type ContextValue = 13 | | Local of GroboIL.Local * Type 14 | | Field of FieldBuilder * Type 15 | | Arg of int * Type 16 | 17 | type Context = 18 | val types : Dictionary 19 | val functions : Dictionary MethodInfo)> 20 | val locals : Dictionary 21 | new (t,f,l) = 22 | {types = t; functions = f; locals = l} 23 | new (ctx : Context) = 24 | let t = new Dictionary(ctx.types) 25 | let f = new Dictionary MethodInfo)>(ctx.functions) 26 | let l = new Dictionary(ctx.locals) 27 | new Context (t,f,l) 28 | new() = 29 | let t = new Dictionary() 30 | let f = new Dictionary MethodInfo)>() 31 | let l = new Dictionary() 32 | new Context (t,f,l) 33 | 34 | member public this.loadAssembly(asm:Assembly) = 35 | let types = List.ofArray (asm.GetTypes()) 36 | List.iter (fun (t:Type) -> this.types.Add(new Symbol(t.FullName),t)) types 37 | 38 | member public this.captureLocal(localName: Symbol, typeBuilder: TypeBuilder) = 39 | let local = this.locals.[localName] 40 | match local with 41 | | Local (_,t) -> 42 | let field = typeBuilder.DefineField(localName.Name,t,FieldAttributes.Static ||| FieldAttributes.Private) 43 | this.locals.[localName] <- Field (field, t) 44 | | Field (fb,_) -> () 45 | | Arg (_,_) -> failwithf "Unable to capture parameter %A" localName.Name 46 | 47 | let create () = 48 | let context = new Context() 49 | context 50 | -------------------------------------------------------------------------------- /Naggum.Compiler/FormGenerator.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.FormGenerator 2 | 3 | open System 4 | open System.Collections.Generic 5 | open System.Reflection 6 | open System.Reflection.Emit 7 | 8 | open GrEmit 9 | 10 | open Naggum.Backend 11 | open Naggum.Backend.Reader 12 | open Naggum.Backend.Matchers 13 | open Naggum.Runtime 14 | open Naggum.Compiler.Context 15 | open Naggum.Compiler.IGenerator 16 | 17 | type FormGenerator() = 18 | interface IGenerator with 19 | member this.Generate _ = failwith "Internal compiler error: unreified form generator invoked" 20 | member this.ReturnTypes () = failwithf "Internal compiler error: inferring return type of unreified form" 21 | 22 | type ValueGenerator(context:Context,value:Value) = 23 | inherit FormGenerator() 24 | interface IGenerator with 25 | member this.Generate _ = failwith "Internal compiler error: unreified value generator invoked" 26 | member this.ReturnTypes () = failwithf "Internal compiler error: inferring return type of unreified value" 27 | 28 | type SymbolGenerator(context:Context,name:string) = 29 | inherit ValueGenerator(context, Reader.Symbol name) 30 | interface IGenerator with 31 | member __.Generate il = 32 | try 33 | let ctxval = context.locals.[Symbol(name)] 34 | match ctxval with 35 | | Local (local, _) -> il.Ldloc local 36 | | Arg (index, _) -> il.Ldarg index 37 | with 38 | | :? KeyNotFoundException -> failwithf "Symbol %A not bound." name 39 | 40 | member this.ReturnTypes () = 41 | match context.locals.[new Symbol(name)] with 42 | |Local (_,t) -> [t] 43 | |Arg (_,t) -> [t] 44 | 45 | type SequenceGenerator (context : Context, seq : SExp list, gf : IGeneratorFactory) = 46 | let rec sequence (il : GroboIL) (seq : SExp list) = 47 | match seq with 48 | | [] -> 49 | () 50 | | [last] -> 51 | let gen = gf.MakeGenerator context last 52 | gen.Generate il 53 | | sexp :: rest -> 54 | let gen = gf.MakeGenerator context sexp 55 | ignore (gen.Generate il) 56 | sequence il rest 57 | 58 | interface IGenerator with 59 | member __.Generate il = sequence il seq 60 | member this.ReturnTypes () = 61 | List.map (fun (sexp) -> List.head ((gf.MakeGenerator context sexp).ReturnTypes())) seq 62 | 63 | type BodyGenerator(context : Context, 64 | resultType : Type, 65 | body : SExp list, 66 | gf : IGeneratorFactory) = 67 | let rec genBody (il : GroboIL) (body : SExp list) = 68 | match body with 69 | | [] -> 70 | if resultType <> typeof then 71 | il.Ldnull () 72 | | [last] -> 73 | let gen = gf.MakeGenerator context last 74 | let stackType = List.head <| gen.ReturnTypes () 75 | gen.Generate il 76 | match (stackType, resultType) with 77 | | (s, r) when s = typeof && r = typeof -> () 78 | | (s, r) when s = typeof && r <> typeof -> il.Ldnull () 79 | | (s, r) when s <> typeof && r = typeof -> il.Pop () 80 | | _ -> () 81 | | sexp :: rest -> 82 | let gen = gf.MakeGenerator context sexp 83 | let val_type = gen.ReturnTypes() 84 | gen.Generate il 85 | if List.head val_type <> typeof then 86 | il.Pop () 87 | genBody il rest 88 | 89 | interface IGenerator with 90 | member __.Generate ilGen = 91 | genBody ilGen body 92 | member __.ReturnTypes () = 93 | match body with 94 | | [] -> [typeof] 95 | | _ -> (gf.MakeGenerator context (List.last body)).ReturnTypes() 96 | 97 | type LetGenerator (context : Context, 98 | resultType : Type, 99 | bindings:SExp, 100 | body : SExp list, 101 | gf : IGeneratorFactory) = 102 | interface IGenerator with 103 | member __.Generate il = 104 | let scope_subctx = new Context (context) 105 | match bindings with 106 | | List list -> 107 | for binding in list do 108 | match binding with 109 | | List [Symbol name; form] -> 110 | let generator = gf.MakeGenerator scope_subctx form 111 | let local_type = List.head (generator.ReturnTypes()) 112 | let local = il.DeclareLocal local_type 113 | scope_subctx.locals.[new Symbol(name)] <- Local (local, local_type) 114 | generator.Generate il 115 | il.Stloc local 116 | | other -> failwithf "In let bindings: Expected: (name (form))\nGot: %A\n" other 117 | | other -> failwithf "In let form: expected: list of bindings\nGot: %A" other 118 | let bodyGen = new BodyGenerator (scope_subctx, resultType, body, gf) :> IGenerator 119 | bodyGen.Generate il 120 | 121 | member this.ReturnTypes () = 122 | let type_subctx = new Context(context) 123 | match bindings with 124 | | List list -> 125 | for binding in list do 126 | match binding with 127 | | List [Symbol name; form] -> 128 | let generator = gf.MakeGenerator type_subctx form 129 | type_subctx.locals.[new Symbol(name)] <- Local (null,generator.ReturnTypes() |> List.head) 130 | | other -> failwithf "In let bindings: Expected: (name (form))\nGot: %A\n" other 131 | | other -> failwithf "In let form: expected: list of bindings\nGot: %A" other 132 | (gf.MakeBody type_subctx body).ReturnTypes() 133 | 134 | type ReducedIfGenerator (context : Context, condition : SExp, ifTrue : SExp, gf : IGeneratorFactory) = 135 | let returnTypes = (gf.MakeGenerator context ifTrue).ReturnTypes() 136 | interface IGenerator with 137 | member __.Generate il = 138 | let condGen = gf.MakeGenerator context condition 139 | let ifTrueGen = gf.MakeGenerator context ifTrue 140 | let ifTrueLbl = il.DefineLabel ("then", true) 141 | let endForm = il.DefineLabel ("endif", true) 142 | condGen.Generate il 143 | il.Brtrue ifTrueLbl 144 | 145 | if List.head returnTypes <> typeof 146 | then il.Ldnull () 147 | 148 | il.Br endForm 149 | il.MarkLabel ifTrueLbl 150 | ifTrueGen.Generate il 151 | il.MarkLabel endForm 152 | 153 | member this.ReturnTypes () = 154 | returnTypes 155 | 156 | type FullIfGenerator (context : Context, condition : SExp, ifTrue : SExp, ifFalse : SExp, gf : IGeneratorFactory) = 157 | interface IGenerator with 158 | member __.Generate il = 159 | let condGen = gf.MakeGenerator context condition 160 | let ifTrueGen = gf.MakeGenerator context ifTrue 161 | let ifFalseGen = gf.MakeGenerator context ifFalse 162 | let ifTrueLbl = il.DefineLabel ("then", true) 163 | let endForm = il.DefineLabel ("endif", true) 164 | ignore (condGen.Generate il) 165 | il.Brtrue ifTrueLbl 166 | ifFalseGen.Generate il 167 | il.Br endForm 168 | il.MarkLabel ifTrueLbl 169 | ifTrueGen.Generate il 170 | il.MarkLabel endForm 171 | 172 | member this.ReturnTypes () = 173 | let true_ret_type = (gf.MakeGenerator context ifTrue).ReturnTypes() 174 | let false_ret_type = (gf.MakeGenerator context ifFalse).ReturnTypes() 175 | List.concat (Seq.ofList [true_ret_type; false_ret_type]) //TODO This should return closest common ancestor of these types 176 | 177 | type FunCallGenerator (context : Context, fname : string, arguments : SExp list, gf : IGeneratorFactory) = 178 | let args = gf.MakeSequence context arguments 179 | let func = context.functions.[new Symbol(fname)] <| args.ReturnTypes() 180 | interface IGenerator with 181 | member __.Generate il = 182 | args.Generate il 183 | il.Call func 184 | 185 | member this.ReturnTypes () = 186 | [func.ReturnType] 187 | 188 | type DefunGenerator (context : Context, 189 | typeBuilder : TypeBuilder, 190 | fname : string, 191 | parameters : SExp list, 192 | body : SExp list, 193 | gf : IGeneratorFactory) = 194 | let bodyGen argTypes = 195 | let methodGen = typeBuilder.DefineMethod(fname, MethodAttributes.Public ||| MethodAttributes.Static, typeof, (Array.ofList argTypes)) 196 | use il = new GroboIL (methodGen) 197 | let fun_ctx = new Context(context) 198 | for parm in parameters do 199 | match parm with 200 | | Symbol paramName -> 201 | let parm_idx = (List.findIndex (fun (p) -> p = parm) parameters) 202 | fun_ctx.locals.[new Symbol(paramName)] <- Arg (parm_idx,argTypes. [parm_idx]) 203 | | _ -> failwithf "In function %A parameter definition:\nExpected: Atom (Symbol)\nGot: %A" fname parm 204 | let methodFactory = gf.MakeGeneratorFactory typeBuilder methodGen 205 | let bodyGen = methodFactory.MakeBody fun_ctx body 206 | bodyGen.Generate il 207 | il.Ret () 208 | methodGen :> MethodInfo 209 | do context.functions.[new Symbol(fname)] <- bodyGen 210 | 211 | interface IGenerator with 212 | member this.Generate ilGen = 213 | () 214 | member this.ReturnTypes() = 215 | [typeof] 216 | 217 | type QuoteGenerator (context : Context, quotedExp : SExp, gf : IGeneratorFactory) = 218 | let generateObject (il : GroboIL) (o : obj) = 219 | let generator = gf.MakeGenerator context (Atom (Object o)) 220 | generator.Generate il 221 | 222 | let generateSymbol (il : GroboIL) (name : string) = 223 | let cons = (typeof).GetConstructor [|typeof|] 224 | il.Ldstr name 225 | il.Newobj cons 226 | 227 | let rec generateList (il : GroboIL) (elements : SExp list) = 228 | let generateListElement e = 229 | match e with 230 | | List l -> generateList il l 231 | | Object o -> generateObject il o 232 | | Symbol s -> generateSymbol il s 233 | let cons = (typeof).GetConstructor(Array.create 2 typeof) 234 | List.last elements |> generateListElement 235 | il.Ldnull () //list terminator 236 | il.Newobj cons 237 | List.rev elements |> List.tail |> List.iter (fun (e) -> 238 | generateListElement e 239 | il.Newobj cons) 240 | 241 | interface IGenerator with 242 | member __.Generate il = 243 | match quotedExp with 244 | | List l -> generateList il l 245 | | Object o -> generateObject il o 246 | | Symbol s -> generateSymbol il s 247 | member this.ReturnTypes () = 248 | match quotedExp with 249 | | List _ -> [typeof] 250 | | Object _ -> [typeof] 251 | | Symbol _ -> [typeof] 252 | 253 | type NewObjGenerator (context : Context, typeName : string, arguments : SExp list, gf : IGeneratorFactory) = 254 | interface IGenerator with 255 | member __.Generate il = 256 | let argsGen = gf.MakeSequence context arguments 257 | let argTypes = argsGen.ReturnTypes() 258 | let objType = 259 | if typeName.StartsWith "System" then 260 | Type.GetType typeName 261 | else 262 | context.types.[new Symbol(typeName)] 263 | ignore <| argsGen.Generate il 264 | il.Newobj <| objType.GetConstructor(Array.ofList argTypes) 265 | 266 | member this.ReturnTypes () = 267 | if typeName.StartsWith "System" then 268 | [Type.GetType typeName] 269 | else 270 | [context.types.[new Symbol(typeName)]] 271 | 272 | type TypeGenerator(context : Context, typeBuilder : TypeBuilder, typeName : string, parentTypeName: string, members : SExp list, gf : IGeneratorFactory) = 273 | let newTypeBuilder = 274 | if parentTypeName = "" then 275 | Globals.ModuleBuilder.DefineType(typeName, TypeAttributes.Class ||| TypeAttributes.Public, typeof) 276 | else 277 | Globals.ModuleBuilder.DefineType(typeName, TypeAttributes.Class ||| TypeAttributes.Public, context.types.[new Symbol(parentTypeName)]) 278 | let mutable fields : string list = [] 279 | 280 | let generate_field field_name = 281 | let fieldBuilder = newTypeBuilder.DefineField(field_name,typeof,FieldAttributes.Public) 282 | fields <- List.append fields [field_name] 283 | let generateMethod method_name method_parms method_body = 284 | let methodGen = newTypeBuilder.DefineMethod(method_name,MethodAttributes.Public, 285 | typeof, 286 | Array.create (List.length method_parms) typeof) 287 | let context = new Context(context) 288 | for parm in method_parms do 289 | match parm with 290 | | Symbol paramName -> 291 | let parm_idx = (List.findIndex (fun (p) -> p = parm) method_parms) 292 | context.locals.[new Symbol(paramName)] <- Arg (parm_idx,typeof) 293 | | _ -> failwithf "In method %A%A parameter definition:\nExpected: Atom(Symbol)\nGot: %A" typeName method_name parm 294 | let newGeneratorFactory = gf.MakeGeneratorFactory newTypeBuilder methodGen 295 | let body_gen = newGeneratorFactory.MakeBody context method_body 296 | use il = new GroboIL (methodGen) 297 | body_gen.Generate il 298 | il.Ret () 299 | 300 | interface IGenerator with 301 | member this.Generate ilGen = 302 | for m in members do 303 | match m with 304 | | List [Symbol "field"; Symbol name] -> generate_field name 305 | | List [Symbol "field"; Symbol access; Symbol name] -> generate_field name 306 | | List (Symbol "method" :: Symbol name :: List parms :: body) -> generateMethod name parms body 307 | | List (Symbol "method" :: Symbol name :: Symbol access :: List parms :: body) -> generateMethod name parms body 308 | | other -> failwithf "In definition of type %A: \nUnknown member definition: %A" typeName other 309 | member this.ReturnTypes () = 310 | [typeof] 311 | -------------------------------------------------------------------------------- /Naggum.Compiler/Generator.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.Generator 2 | 3 | open System 4 | open System.IO 5 | open System.Reflection 6 | open System.Reflection.Emit 7 | 8 | open GrEmit 9 | 10 | open Naggum.Backend 11 | open Naggum.Backend.Reader 12 | open Naggum.Compiler.IGenerator 13 | open Naggum.Compiler.GeneratorFactory 14 | 15 | let compileMethod context (generatorFactory : IGeneratorFactory) body (methodBuilder : MethodBuilder) fileName = 16 | use il = new GroboIL (methodBuilder) 17 | try 18 | let gen = generatorFactory.MakeBody context body 19 | gen.Generate il 20 | with 21 | | ex -> printfn "File: %A\nForm: %A\nError: %A" fileName sexp ex.Source 22 | 23 | il.Ret () 24 | 25 | let compile (source : Stream) (assemblyName : string) (filePath : string) (asmRefs:string list): unit = 26 | let assemblyName = AssemblyName assemblyName 27 | let path = Path.GetDirectoryName filePath 28 | let assemblyPath = if path = "" then null else path 29 | let fileName = Path.GetFileName filePath 30 | let appDomain = AppDomain.CurrentDomain 31 | 32 | let assemblyBuilder = appDomain.DefineDynamicAssembly (assemblyName, AssemblyBuilderAccess.Save, assemblyPath) 33 | Globals.ModuleBuilder <- assemblyBuilder.DefineDynamicModule(assemblyBuilder.GetName().Name, fileName) 34 | let typeBuilder = Globals.ModuleBuilder.DefineType("Program", TypeAttributes.Public ||| TypeAttributes.Class ||| TypeAttributes.BeforeFieldInit) 35 | let methodBuilder = typeBuilder.DefineMethod ("Main", 36 | MethodAttributes.Public ||| MethodAttributes.Static, 37 | typeof, 38 | [| |]) 39 | 40 | let gf = new GeneratorFactory(typeBuilder, methodBuilder) :> IGeneratorFactory 41 | assemblyBuilder.SetEntryPoint methodBuilder 42 | 43 | let context = Context.create () 44 | 45 | //loading language runtime 46 | let rta = Assembly.LoadFrom("Naggum.Runtime.dll") 47 | context.loadAssembly rta 48 | 49 | // Load .NET runtime and all referenced assemblies: 50 | context.loadAssembly <| Assembly.Load "mscorlib" 51 | List.iter context.loadAssembly (List.map Assembly.LoadFrom asmRefs) 52 | 53 | let body = Reader.parse fileName source 54 | compileMethod context gf body methodBuilder fileName 55 | 56 | typeBuilder.CreateType() 57 | |> ignore 58 | 59 | assemblyBuilder.Save fileName 60 | -------------------------------------------------------------------------------- /Naggum.Compiler/GeneratorFactory.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.GeneratorFactory 2 | 3 | open System 4 | open System.Reflection.Emit 5 | open System.Text.RegularExpressions 6 | 7 | open Naggum.Backend 8 | open Naggum.Backend.MaybeMonad 9 | open Naggum.Backend.Reader 10 | open Naggum.Backend.Matchers 11 | open Naggum.Compiler.ClrGenerator 12 | open Naggum.Compiler.Context 13 | open Naggum.Compiler.FormGenerator 14 | open Naggum.Compiler.IGenerator 15 | open Naggum.Compiler.MathGenerator 16 | open Naggum.Compiler.NumberGen 17 | open Naggum.Compiler.StringGen 18 | open Naggum.Runtime 19 | 20 | type GeneratorFactory(typeBuilder : TypeBuilder, 21 | methodBuilder : MethodBuilder) = 22 | member private this.makeObjectGenerator(o:obj) = 23 | match o with 24 | | :? System.Int32 -> 25 | (new Int32Gen(o :?> System.Int32)) :> IGenerator 26 | | :? System.Int64 -> 27 | (new Int64Gen(o :?> System.Int64)) :> IGenerator 28 | | :? System.Single -> 29 | (new SingleGen(o :?> System.Single)) :> IGenerator 30 | | :? System.Double -> 31 | (new DoubleGen(o :?> System.Double)) :> IGenerator 32 | | :? System.String -> 33 | (new StringGen(o :?> System.String)) :> IGenerator 34 | | other -> failwithf "Not a basic value: %A\n" other 35 | 36 | member private this.makeValueGenerator (context: Context, value:Value) = 37 | match value with 38 | | Reader.Symbol name -> 39 | (new SymbolGenerator(context,name)) :> IGenerator 40 | | Reader.Object o -> this.makeObjectGenerator o 41 | 42 | member private this.MakeFormGenerator (context : Context, form : SExp list) : IGenerator = 43 | match form with 44 | | Symbol "defun" :: Symbol name :: List args :: body -> 45 | upcast new DefunGenerator (context, typeBuilder, name, args, body, this) 46 | | [Symbol "if"; condition; ifTrue; ifFalse] -> // full if form 47 | upcast new FullIfGenerator (context, condition, ifTrue, ifFalse, this) 48 | | [Symbol "if"; condition; ifTrue] -> // reduced if form 49 | upcast new ReducedIfGenerator (context, condition, ifTrue, this) 50 | | Symbol "let" :: bindings :: body -> // let form 51 | upcast new LetGenerator (context, 52 | typeof, 53 | bindings, 54 | body, 55 | this) 56 | | [Symbol "quote"; quotedExp] -> 57 | upcast new QuoteGenerator (context, quotedExp, this) 58 | | Symbol "new" :: Symbol typeName :: args -> 59 | upcast new NewObjGenerator(context, typeName, args, this) 60 | | Symbol "+" :: args -> 61 | upcast new ArithmeticGenerator (context, args, Add, this) 62 | | Symbol "-" :: args -> 63 | upcast new ArithmeticGenerator (context, args, Sub, this) 64 | | Symbol "*" :: args -> 65 | upcast new ArithmeticGenerator (context, args, Mul, this) 66 | | Symbol "/" :: args -> 67 | upcast new ArithmeticGenerator (context, args, Div, this) 68 | | Symbol "=" :: argA :: argB :: [] -> 69 | upcast new SimpleLogicGenerator (context, argA, argB, Ceq, this) 70 | | Symbol "<" :: argA :: argB :: [] -> 71 | upcast new SimpleLogicGenerator (context, argA, argB, Clt, this) 72 | | Symbol ">" :: argA :: argB :: [] -> 73 | upcast new SimpleLogicGenerator (context, argA, argB, Cgt, this) 74 | | Symbol "call" :: Symbol fname :: instance :: args -> 75 | upcast new InstanceCallGenerator (context, instance, fname, args, this) 76 | | Symbol fname :: args -> // generic funcall pattern 77 | let tryGetType typeName = 78 | try Some (context.types.[Symbol(typeName)]) with 79 | | _ -> 80 | try Some (Type.GetType typeName) with 81 | | _ -> None 82 | 83 | let callRegex = Regex(@"([\w\.]+)\.(\w+)", RegexOptions.Compiled) 84 | let callMatch = callRegex.Match fname 85 | let maybeClrType = 86 | maybe { 87 | let! typeName = if callMatch.Success then Some callMatch.Groups.[1].Value else None 88 | let! clrType = tryGetType typeName 89 | return clrType 90 | } 91 | 92 | if Option.isSome maybeClrType then 93 | let clrType = Option.get maybeClrType 94 | let methodName = callMatch.Groups.[2].Value 95 | upcast new ClrCallGenerator (context, clrType, methodName, args, this) 96 | else 97 | upcast new FunCallGenerator(context, fname, args, this) 98 | | _ -> failwithf "Form %A is not supported yet" list 99 | 100 | member private this.MakeSequenceGenerator (context: Context, seq : SExp list) = 101 | new SequenceGenerator (context, seq, (this :> IGeneratorFactory)) 102 | 103 | member private this.MakeBodyGenerator (context : Context, body : SExp list) = 104 | new BodyGenerator (context, methodBuilder.ReturnType, body, this) 105 | 106 | interface IGeneratorFactory with 107 | member this.MakeGenerator context sexp = 108 | match sexp with 109 | | Atom value -> this.makeValueGenerator (context, value) 110 | | List form -> this.MakeFormGenerator (context,form) 111 | 112 | member this.MakeSequence context seq = this.MakeSequenceGenerator (context,seq) :> IGenerator 113 | 114 | member this.MakeBody context body = this.MakeBodyGenerator (context,body) :> IGenerator 115 | 116 | member this.MakeGeneratorFactory newTypeBuilder newMethodBuilder = 117 | new GeneratorFactory(newTypeBuilder, 118 | newMethodBuilder) :> IGeneratorFactory 119 | -------------------------------------------------------------------------------- /Naggum.Compiler/Globals.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.Globals 2 | 3 | open System.Reflection.Emit 4 | 5 | let mutable ModuleBuilder:ModuleBuilder = null -------------------------------------------------------------------------------- /Naggum.Compiler/IGenerator.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.IGenerator 2 | 3 | open System 4 | open System.Reflection.Emit 5 | 6 | open GrEmit 7 | 8 | open Naggum.Backend.Reader 9 | open Naggum.Compiler.Context 10 | 11 | //TODO: Add a method that returns generated values' types without actually emitting the code. 12 | type IGenerator = 13 | interface 14 | abstract ReturnTypes : unit -> Type list 15 | abstract Generate : GroboIL -> unit 16 | end 17 | 18 | type IGeneratorFactory = 19 | interface 20 | abstract MakeGenerator : Context -> SExp -> IGenerator 21 | abstract MakeSequence : Context -> SExp list -> IGenerator 22 | abstract MakeBody : Context -> SExp list -> IGenerator 23 | abstract MakeGeneratorFactory : TypeBuilder -> MethodBuilder -> IGeneratorFactory 24 | end 25 | -------------------------------------------------------------------------------- /Naggum.Compiler/MathGenerator.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.MathGenerator 2 | 3 | open Naggum.Backend.Reader 4 | open Naggum.Compiler.Context 5 | open Naggum.Compiler.IGenerator 6 | 7 | type ArithmeticOperation = 8 | | Add 9 | | Div 10 | | Mul 11 | | Sub 12 | 13 | type LogicOperation = 14 | | Ceq 15 | | Cgt 16 | | Clt 17 | 18 | //TODO: Make this useful; i.e. determine eldest type in a numeric tower and convert all junior types to eldest 19 | let tower = dict [(typeof, 1); (typeof, 2); (typeof, 3); (typeof, 4); (typeof,5)] 20 | 21 | let maxType types = 22 | try 23 | let maxOrder = List.maxBy (fun (t) -> tower.[t]) types 24 | (Seq.find (fun (KeyValue(o,_)) -> o = maxOrder) tower).Key 25 | with 26 | | :? System.Collections.Generic.KeyNotFoundException -> failwithf "Some types of %A are not suitable in an arithmetic expression." types 27 | 28 | type ArithmeticGenerator (context : Context, 29 | args : SExp list, 30 | operation : ArithmeticOperation, 31 | gf : IGeneratorFactory) = 32 | interface IGenerator with 33 | member __.Generate il = 34 | // making this just for the sake of return types 35 | let maxType = (gf.MakeSequence context args).ReturnTypes() |> maxType 36 | // loading first arg manually so it won't be succeeded by operation opcode 37 | let argGen = gf.MakeGenerator context (List.head args) 38 | let argType = argGen.ReturnTypes() |> List.head 39 | argGen.Generate il 40 | if argType <> maxType then 41 | il.Newobj <| maxType.GetConstructor [|argType|] 42 | for arg in List.tail args do 43 | let argGen = gf.MakeGenerator context arg 44 | let argType = argGen.ReturnTypes() |> List.head 45 | argGen.Generate il 46 | match operation with 47 | | Add -> il.Add () 48 | | Div -> il.Div false 49 | | Mul -> il.Mul () 50 | | Sub -> il.Sub () 51 | 52 | member this.ReturnTypes () = 53 | [List.map (fun (sexp) -> (gf.MakeGenerator context sexp).ReturnTypes() |> List.head) args |> maxType] 54 | 55 | type SimpleLogicGenerator (context : Context, 56 | argA : SExp, 57 | argB : SExp, 58 | operation : LogicOperation, 59 | gf : IGeneratorFactory) = 60 | interface IGenerator with 61 | member __.Generate il = 62 | let aGen = gf.MakeGenerator context argA 63 | let bGen = gf.MakeGenerator context argB 64 | aGen.Generate il |> ignore 65 | bGen.Generate il |> ignore 66 | match operation with 67 | | Ceq -> il.Ceq () 68 | | Cgt -> il.Cgt false 69 | | Clt -> il.Clt false 70 | 71 | member this.ReturnTypes () = 72 | [typeof] 73 | -------------------------------------------------------------------------------- /Naggum.Compiler/Naggum.Compiler.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {a4269c5e-e4ac-44bf-a06e-1b45248910ad} 9 | Exe 10 | ngc 11 | Naggum.Compiler 12 | v4.6.1 13 | 14 | 15 | Naggum.Compiler 16 | 17 | 18 | 4.4.0.0 19 | 11 20 | 21 | 22 | true 23 | full 24 | false 25 | false 26 | bin\Debug\ 27 | DEBUG;TRACE 28 | 3 29 | AnyCPU 30 | bin\Debug\Naggum.Compiler.XML 31 | ..\..\..\tests\test.naggum 32 | 33 | 34 | pdbonly 35 | true 36 | true 37 | bin\Release\ 38 | TRACE 39 | 3 40 | AnyCPU 41 | bin\Release\Naggum.Compiler.XML 42 | 43 | 44 | 45 | 46 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 47 | 48 | 49 | 50 | 51 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 52 | 53 | 54 | 55 | 56 | 57 | 58 | Always 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | ..\packages\FParsec.1.0.2\lib\net40-client\FParsec.dll 77 | True 78 | 79 | 80 | ..\packages\FParsec.1.0.2\lib\net40-client\FParsecCS.dll 81 | True 82 | 83 | 84 | ..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll 85 | True 86 | 87 | 88 | ..\packages\GrEmit.2.1.7\lib\net40\GrEmit.dll 89 | True 90 | 91 | 92 | 93 | 94 | 95 | 96 | Naggum.Backend 97 | {243738f3-d798-4b09-8797-f90b21414b60} 98 | True 99 | 100 | 101 | Naggum.Runtime 102 | {402b5e79-e063-4833-ae4b-2986aeec1d75} 103 | True 104 | 105 | 106 | 113 | -------------------------------------------------------------------------------- /Naggum.Compiler/NumberGen.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.NumberGen 2 | 3 | open IGenerator 4 | open System 5 | 6 | type NumberGen<'TNumber> (number: 'TNumber) = 7 | interface IGenerator with 8 | member this.Generate _ = failwith "Failure: Tried to generate unreified number constant.\n" 9 | member this.ReturnTypes () = [typeof<'TNumber>] 10 | 11 | type Int32Gen (number: Int32) = 12 | inherit NumberGen(number) 13 | interface IGenerator with 14 | member __.Generate il = il.Ldc_I4 number 15 | 16 | type Int64Gen (number: Int64) = 17 | inherit NumberGen(number) 18 | interface IGenerator with 19 | member __.Generate il = il.Ldc_I8 number 20 | 21 | type SingleGen (number: Single) = 22 | inherit NumberGen(number) 23 | interface IGenerator with 24 | member __.Generate il = il.Ldc_R4 number 25 | 26 | type DoubleGen (number: Double) = 27 | inherit NumberGen(number) 28 | interface IGenerator with 29 | member __.Generate il = il.Ldc_R8 number 30 | -------------------------------------------------------------------------------- /Naggum.Compiler/Program.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.Program 2 | 3 | open System 4 | open System.IO 5 | open Naggum.Compiler.Generator 6 | 7 | let args = List.ofArray (Environment.GetCommandLineArgs()) 8 | let mutable sources = [] 9 | let mutable asmRefs = [] 10 | for arg in (List.tail args) do 11 | if arg.StartsWith "/r:" then 12 | asmRefs <- arg.Replace("/r:","") :: asmRefs 13 | else 14 | sources <- arg :: sources 15 | for fileName in sources do 16 | let source = File.Open (fileName,FileMode.Open) :> Stream 17 | let assemblyName = Path.GetFileNameWithoutExtension fileName 18 | Generator.compile source assemblyName (assemblyName + ".exe") asmRefs 19 | source.Close() 20 | -------------------------------------------------------------------------------- /Naggum.Compiler/StringGen.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Compiler.StringGen 2 | 3 | open IGenerator 4 | 5 | type StringGen (str : string) = 6 | interface IGenerator with 7 | member __.Generate il = 8 | il.Ldstr str 9 | member this.ReturnTypes () = 10 | [typeof] 11 | -------------------------------------------------------------------------------- /Naggum.Compiler/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Naggum.Interactive/Naggum.Interactive.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {34E781DF-EAD9-4034-ADE4-8DA41A16644A} 9 | Exe 10 | Properties 11 | Naggum.Interactive 12 | Naggum.Interactive 13 | v4.6.1 14 | 15 | 16 | 512 17 | 18 | 19 | true 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | full 23 | AnyCPU 24 | ..\bin\ngi.exe.CodeAnalysisLog.xml 25 | true 26 | GlobalSuppressions.cs 27 | prompt 28 | MinimumRecommendedRules.ruleset 29 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 30 | true 31 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 32 | true 33 | false 34 | false 35 | bin\Debug\Naggum.Interactive.XML 36 | 37 | 38 | bin\Release\ 39 | TRACE 40 | true 41 | pdbonly 42 | AnyCPU 43 | ..\bin\ngi.exe.CodeAnalysisLog.xml 44 | true 45 | GlobalSuppressions.cs 46 | prompt 47 | MinimumRecommendedRules.ruleset 48 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 49 | true 50 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 51 | true 52 | false 53 | false 54 | bin\Release\Naggum.Interactive.XML 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | {402B5E79-E063-4833-AE4B-2986AEEC1D75} 72 | Naggum.Runtime 73 | 74 | 75 | 76 | 77 | 78 | 79 | 86 | -------------------------------------------------------------------------------- /Naggum.Interactive/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Naggum.Runtime; 4 | 5 | namespace Naggum.Interactive 6 | { 7 | class Program 8 | { 9 | static void Main(string[] args) 10 | { 11 | Stream input = System.Console.OpenStandardInput(); 12 | for (; ; ) 13 | { 14 | System.Console.Out.Write(">"); 15 | Object obj = Reader.Read(input); 16 | System.Console.Out.WriteLine(obj.ToString()); 17 | } 18 | input.Close(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Naggum.Interactive/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("Naggum.Interactive")] 5 | [assembly: AssemblyProduct("Naggum")] 6 | [assembly: AssemblyCopyright("Copyright © Naggum authors 2013–2016")] 7 | [assembly: ComVisible(false)] 8 | [assembly: Guid("fe6d0e11-90e5-4715-9a40-878ecf803b52")] 9 | [assembly: AssemblyVersion("0.0.1.0")] 10 | -------------------------------------------------------------------------------- /Naggum.Interactive/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Naggum.Runtime/Cons.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections; 4 | 5 | namespace Naggum.Runtime 6 | { 7 | /// 8 | /// Cons-cell and basic cons manipulation functions. 9 | /// 10 | public class Cons : IEquatable 11 | { 12 | public Object pCar { get; set; } 13 | public Object pCdr { get; set; } 14 | 15 | /// 16 | /// The real cons-cell constructor. 17 | /// 18 | /// CAR part of the new cell 19 | /// CDR part of the new cell 20 | public Cons(Object aCar, Object aCdr) 21 | { 22 | pCar = aCar; 23 | pCdr = aCdr; 24 | } 25 | 26 | /// 27 | /// 28 | /// Cons-cell 29 | /// CAR part of given cell 30 | public static Object Car(Cons aCons) 31 | { 32 | return aCons.pCar; 33 | } 34 | /// 35 | /// 36 | /// Cons-cell 37 | /// CDR part of given cell 38 | public static Object Cdr(Cons aCons) 39 | { 40 | return aCons.pCdr; 41 | } 42 | 43 | /// 44 | /// Checks if the cons-cell is a list 45 | /// 46 | /// Cons-cell 47 | /// True if CDR part of the cell is a list or is null. 48 | /// False otherwise. 49 | public static bool IsList(Cons aCons) 50 | { 51 | if (aCons == null) return true; //Empty list is still a list. 52 | if (aCons.pCdr == null) return true; //List with one element is a list; 53 | else if (aCons.pCdr.GetType() == typeof(Cons)) return IsList((Cons)aCons.pCdr); 54 | else return false; //If it's not null or not a list head, then it's definitely not a list. 55 | } 56 | 57 | /// 58 | /// Converts cons-cell to string representation. 59 | /// 60 | /// String representation of cons-cell. 61 | public override String ToString() 62 | { 63 | StringBuilder buffer = new StringBuilder(""); 64 | buffer.Append("("); 65 | if (IsList(this)) 66 | { 67 | for (Cons it = this; it != null; it = (Cons)it.pCdr) 68 | { 69 | buffer.Append(it.pCar.ToString()); 70 | if (it.pCdr != null) buffer.Append(" "); 71 | } 72 | } 73 | else 74 | { 75 | buffer.Append(pCar.ToString()).Append(" . ").Append(pCdr.ToString()); 76 | } 77 | buffer.Append(")"); 78 | return buffer.ToString(); 79 | } 80 | 81 | /// 82 | /// Checks cons cell for equality with other cell. 83 | /// 84 | /// Other cons cell 85 | /// True if other cell is equal to this; false otherwise. 86 | bool IEquatable.Equals(Cons other) 87 | { 88 | return pCar == other.pCar && pCdr == other.pCdr; 89 | } 90 | 91 | /// 92 | /// Constructs a list. 93 | /// 94 | /// Elements of a list. 95 | /// List with given elements. 96 | public static Cons List(params object[] elements) 97 | { 98 | Cons list = null; 99 | Array.Reverse(elements); 100 | foreach (var element in elements) 101 | { 102 | var tmp = new Cons(element, list); 103 | list = tmp; 104 | } 105 | return list; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Naggum.Runtime/Naggum.Runtime.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {402B5E79-E063-4833-AE4B-2986AEEC1D75} 9 | Library 10 | Properties 11 | Naggum.Runtime 12 | Naggum.Runtime 13 | v4.6.1 14 | 512 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 59 | -------------------------------------------------------------------------------- /Naggum.Runtime/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("Naggum.Runtime")] 5 | [assembly: AssemblyProduct("Naggum")] 6 | [assembly: AssemblyCopyright("Copyright © Naggum authors 2011–2016")] 7 | [assembly: ComVisible(false)] 8 | [assembly: Guid("4d6a7899-d5cd-4df7-8bab-dbc94b258e57")] 9 | [assembly: AssemblyVersion("0.0.1.0")] 10 | -------------------------------------------------------------------------------- /Naggum.Runtime/Reader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Naggum.Runtime 8 | { 9 | public class Reader 10 | { 11 | /// 12 | /// Checks if the character is constituent, i.e. not whitespace or list separator. 13 | /// 14 | /// character to be checked 15 | /// true if the character is constituent, false otherwise 16 | public static bool isConstituent(char c) 17 | { 18 | return (!Char.IsWhiteSpace(c)) 19 | && c != '(' 20 | && c != ')'; 21 | } 22 | 23 | /// 24 | /// Reads a symbol from a stream. 25 | /// 26 | /// stream to read from 27 | /// 28 | private static Object ReadSymbol(StreamReader reader) 29 | { 30 | bool in_symbol = true; 31 | StringBuilder symbol_name = new StringBuilder(); 32 | while (in_symbol) 33 | { 34 | var ch = reader.Peek(); 35 | if (ch < 0) throw new IOException("Unexpected end of stream."); 36 | if (isConstituent((char)ch)) 37 | { 38 | symbol_name.Append((char)reader.Read()); 39 | } 40 | else 41 | { 42 | in_symbol = false; 43 | } 44 | } 45 | if (symbol_name.Length > 0) 46 | return new Symbol(symbol_name.ToString()); 47 | else 48 | throw new IOException("Empty symbol."); 49 | } 50 | 51 | /// 52 | /// Reads a list from input stream. 53 | /// 54 | /// stream to read from 55 | /// 56 | private static Object ReadList(StreamReader reader) 57 | { 58 | bool in_list = true; 59 | Stack list_stack = new Stack(); 60 | Cons list = null; 61 | while (in_list) 62 | { 63 | var ch = reader.Peek(); 64 | if (ch < 0) throw new IOException("Unexpected end of stream."); 65 | if ((char)ch != ')') 66 | { 67 | list_stack.Push(ReadObject(reader)); 68 | } 69 | else 70 | { 71 | reader.Read(); //consume closing paren 72 | in_list = false; 73 | } 74 | } 75 | while (list_stack.Count > 0) list = new Cons(list_stack.Pop(), list); 76 | return list; 77 | } 78 | 79 | /// 80 | /// Reads a string from input stream 81 | /// 82 | /// input stream 83 | /// a string that was read 84 | private static string ReadString(StreamReader reader) 85 | { 86 | bool in_string = true; 87 | bool single_escape = false; 88 | StringBuilder sbld = new StringBuilder(); 89 | 90 | while (in_string) 91 | { 92 | var ch = reader.Read(); 93 | if (single_escape) 94 | { 95 | single_escape = false; 96 | switch (ch) 97 | { 98 | case 'n': sbld.Append('\n'); break; 99 | case 'r': sbld.Append('\r'); break; 100 | case '\"': sbld.Append('"'); break; 101 | case 't': sbld.Append('\t'); break; 102 | case '\\': sbld.Append('\\'); break; 103 | default: throw new Exception("Unknown escape sequence: \\" + ch); 104 | } 105 | } 106 | else 107 | { 108 | switch (ch) 109 | { 110 | case '\"': in_string = false; break; 111 | case '\\': single_escape = true; break; 112 | default: sbld.Append((char)ch); break; 113 | } 114 | } 115 | 116 | } 117 | return sbld.ToString(); 118 | } 119 | 120 | private static Object ReadObject(StreamReader reader) 121 | { 122 | while (Char.IsWhiteSpace((char)reader.Peek())) reader.Read(); //consume all leading whitespace 123 | var ch = reader.Peek(); 124 | if (ch < 0) return null; 125 | if (ch == '(') //beginning of a list 126 | { 127 | reader.Read(); //consume opening list delimiter. 128 | return ReadList(reader); 129 | } 130 | if (ch == '\"') //beginning of a string 131 | { 132 | reader.Read(); //consume opening quote 133 | return ReadString(reader); 134 | } 135 | if (isConstituent((char)ch)) 136 | return ReadSymbol(reader); 137 | 138 | throw new IOException("Unexpected char: " + (char)ch); 139 | } 140 | 141 | /// 142 | /// Reads an object from input stream. 143 | /// 144 | /// stream to read from 145 | /// 146 | public static Object Read(Stream stream) 147 | { 148 | StreamReader reader = new StreamReader(stream); 149 | var obj = ReadObject(reader); 150 | return obj; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Naggum.Runtime/Symbol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Naggum.Runtime 4 | { 5 | public class Symbol : IEquatable 6 | { 7 | public String Name { get; set; } 8 | /// 9 | /// Constructs new symbol object. 10 | /// 11 | /// Symbol name 12 | public Symbol(String aName) 13 | { 14 | Name = aName; 15 | } 16 | 17 | bool IEquatable.Equals(Symbol other) 18 | { 19 | return AreEqual(this, other); 20 | } 21 | 22 | public override bool Equals(object obj) 23 | { 24 | var symbol = obj as Symbol; 25 | if (symbol != null) 26 | { 27 | return AreEqual(this, symbol); 28 | } 29 | 30 | return false; 31 | } 32 | 33 | public override int GetHashCode() 34 | { 35 | return Name.GetHashCode(); 36 | } 37 | 38 | /// 39 | /// 40 | /// Returns symbol's name as string. 41 | public override string ToString() 42 | { 43 | return Name; 44 | } 45 | 46 | private static bool AreEqual(Symbol one, Symbol other) 47 | { 48 | return one.Name.Equals(other.Name); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Naggum.Test/AssemblerTests.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Test.AssemblerTests 2 | 3 | open System.IO 4 | open System.Reflection.Emit 5 | open System.Text 6 | 7 | open Xunit 8 | 9 | open Naggum.Assembler 10 | 11 | let assemble (source : string) = 12 | use stream = new MemoryStream(Encoding.UTF8.GetBytes source) 13 | let repr = Processor.prepare "file.ngi" stream 14 | Assembler.assemble AssemblyBuilderAccess.Save (Seq.exactlyOne repr) 15 | 16 | let execute source = 17 | let fileName = "file.exe" 18 | let assembly = assemble source 19 | assembly.Save fileName 20 | 21 | Process.run fileName 22 | 23 | [] 24 | let ``Empty assembly should be assembled`` () = 25 | let source = "(.assembly Empty)" 26 | let result = assemble source 27 | Assert.NotNull result 28 | 29 | [] 30 | let ``Hello world program should be executed`` () = 31 | let source = "(.assembly Hello 32 | (.method Main () System.Void (.entrypoint) 33 | (ldstr \"Hello, world!\") 34 | (call (mscorlib System.Console WriteLine (System.String) System.Void)) 35 | (ret))) 36 | " 37 | let output = execute source 38 | Assert.Equal ("Hello, world!\n", output) 39 | 40 | [] 41 | let ``Sum program should be executed`` () = 42 | let source = "(.assembly Sum 43 | (.method Main () System.Void (.entrypoint) 44 | (ldc.i4 10) 45 | (ldc.i4 20) 46 | (ldc.i4 30) 47 | (add) 48 | (add) 49 | (call (mscorlib System.Console WriteLine (System.Int32) System.Void)) 50 | (ret))) 51 | " 52 | let output = execute source 53 | Assert.Equal ("60\n", output) 54 | -------------------------------------------------------------------------------- /Naggum.Test/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace Naggum.Backend.AssemblyInfo 2 | 3 | open System.Reflection 4 | open System.Runtime.InteropServices 5 | 6 | [] 7 | [] 8 | [] 9 | [] 10 | [] 11 | [] 12 | 13 | () 14 | -------------------------------------------------------------------------------- /Naggum.Test/CompilerTest.fs: -------------------------------------------------------------------------------- 1 | namespace Naggum.Test 2 | 3 | open System 4 | open System.Diagnostics 5 | open System.IO 6 | 7 | open Xunit 8 | 9 | open Naggum.Compiler 10 | 11 | type CompilerTest() = 12 | static let testExtension = "naggum" 13 | static let resultExtension = "result" 14 | static let executableExtension = "exe" 15 | 16 | static let directory = Path.Combine ("..", "..", "..", "tests") 17 | static let filenames = [@"comment"; @"test"; "let-funcall"] 18 | 19 | static member private RunTest testName = 20 | let basePath = Path.Combine(directory, testName) 21 | let testPath = Path.ChangeExtension(basePath, testExtension) 22 | let resultPath = Path.ChangeExtension(basePath, resultExtension) 23 | let executableName = Path.ChangeExtension (testName, executableExtension) 24 | let executablePath = Path.Combine (Environment.CurrentDirectory, executableName) 25 | 26 | use stream = File.Open(testPath, FileMode.Open) 27 | Generator.compile stream testName executablePath [] 28 | 29 | let result = Process.run executablePath 30 | 31 | let reference = (File.ReadAllText resultPath).Replace("\r\n", "\n") 32 | Assert.Equal(reference, result) 33 | 34 | [] 35 | member this.RunTests() = 36 | filenames 37 | |> List.iter CompilerTest.RunTest 38 | -------------------------------------------------------------------------------- /Naggum.Test/InstructionTests.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Test.InstructionTests 2 | 3 | open System.IO 4 | open System.Reflection.Emit 5 | open System.Text 6 | 7 | open Xunit 8 | 9 | open Naggum.Assembler 10 | 11 | let checkResult (body : string) (expectedResult : obj) = 12 | // TODO: FSCheck tests 13 | let source = 14 | sprintf 15 | <| "(.assembly TestAssembly 16 | (.method Test () %s () 17 | %s) 18 | )" 19 | <| (expectedResult.GetType().FullName) 20 | <| body 21 | use stream = new MemoryStream(Encoding.UTF8.GetBytes source) 22 | let repr = Processor.prepare "file.ngi" stream |> Seq.exactlyOne 23 | let assembly = Assembler.assemble AssemblyBuilderAccess.RunAndCollect repr 24 | 25 | // There's also a dymanic manifest module in a dynamic assembly; we need to filter that. 26 | let ``module`` = 27 | assembly.GetModules () 28 | |> Seq.filter (fun m -> m.ScopeName = assembly.GetName().Name) 29 | |> Seq.exactlyOne 30 | 31 | let ``method`` = ``module``.GetMethod "Test" 32 | let result = ``method``.Invoke (null, [| |]) 33 | Assert.Equal (expectedResult, result) 34 | 35 | [] 36 | [] 37 | [] 38 | [] 39 | [] 40 | let ``Integer math should work properly`` (code, result) = 41 | checkResult code result 42 | -------------------------------------------------------------------------------- /Naggum.Test/MatchersTests.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Test.MatchersTests 2 | 3 | open Xunit 4 | 5 | open Naggum.Backend 6 | open Naggum.Backend.Matchers 7 | 8 | [] 9 | let ``String should be matched by Object matcher`` () = 10 | let string = Reader.Atom (Reader.Object "xxx") 11 | let isObject = 12 | match string with 13 | | Object _ -> true 14 | | _ -> false 15 | 16 | Assert.True isObject 17 | -------------------------------------------------------------------------------- /Naggum.Test/Naggum.Test.dll.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Naggum.Test/Naggum.Test.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {38230cbd-de3e-4470-925f-de966e8691aa} 9 | Library 10 | Naggum.Test 11 | Naggum.Test 12 | v4.6.1 13 | Naggum.Test 14 | 4.4.0.0 15 | 11 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | 3 26 | bin\Debug\Naggum.Test.XML 27 | 28 | 29 | pdbonly 30 | true 31 | true 32 | bin\Release\ 33 | TRACE 34 | 3 35 | bin\Release\Naggum.Test.XML 36 | 37 | 38 | 39 | 40 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets 41 | 42 | 43 | 44 | 45 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets 46 | 47 | 48 | 49 | 50 | 51 | 52 | PreserveNewest 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll 66 | True 67 | 68 | 69 | 70 | 71 | 72 | 73 | ..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll 74 | True 75 | 76 | 77 | ..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll 78 | True 79 | 80 | 81 | ..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll 82 | True 83 | 84 | 85 | ..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll 86 | True 87 | 88 | 89 | 90 | 91 | Naggum.Assembler 92 | {40b84f1e-1823-4255-80d4-1297613025c1} 93 | True 94 | 95 | 96 | Naggum.Backend 97 | {243738f3-d798-4b09-8797-f90b21414b60} 98 | True 99 | 100 | 101 | Naggum.Compiler 102 | {a4269c5e-e4ac-44bf-a06e-1b45248910ad} 103 | True 104 | 105 | 106 | 113 | -------------------------------------------------------------------------------- /Naggum.Test/Process.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Test.Process 2 | 3 | open System.Diagnostics 4 | 5 | let run fileName = 6 | // TODO: Mono check 7 | let startInfo = ProcessStartInfo (fileName, 8 | UseShellExecute = false, 9 | RedirectStandardOutput = true) 10 | use p = Process.Start startInfo 11 | p.WaitForExit () 12 | p.StandardOutput.ReadToEnd().Replace ("\r\n", "\n") 13 | -------------------------------------------------------------------------------- /Naggum.Test/ProcessorTests.fs: -------------------------------------------------------------------------------- 1 | module Naggum.Test.ProcessorTests 2 | 3 | open System 4 | open System.IO 5 | open System.Reflection 6 | open System.Text 7 | 8 | open Xunit 9 | 10 | open Naggum.Assembler 11 | open Naggum.Assembler.Representation 12 | 13 | let mscorlib = Assembly.GetAssembly(typeof) 14 | 15 | let mainMethodDefinition = 16 | { Metadata = Set.singleton EntryPoint 17 | Visibility = Public 18 | Name = "Main" 19 | ArgumentTypes = List.empty 20 | ReturnType = typeof 21 | Body = List.empty } 22 | 23 | let consoleWriteLine = 24 | { Assembly = Some mscorlib 25 | ContainingType = Some typeof 26 | Name = "WriteLine" 27 | ArgumentTypes = [typeof] 28 | ReturnType = typeof } 29 | 30 | let checkPreparationResult (source : string) (expected : Assembly list) = 31 | use stream = new MemoryStream(Encoding.UTF8.GetBytes source) 32 | let actual = Processor.prepare "file.ngi" stream |> Seq.toList 33 | 34 | Assert.Equal (expected.ToString (), actual.ToString ()) // for diagnostic 35 | Assert.Equal (expected, actual) 36 | 37 | [] 38 | let ``Empty assembly should be processed`` () = 39 | let source = "(.assembly Empty)" 40 | let result = { Name = "Empty"; Units = List.empty } 41 | checkPreparationResult source [result] 42 | 43 | [] 44 | let ``Simplest method should be processed`` () = 45 | let source = "(.assembly Stub 46 | (.method Main () System.Void (.entrypoint) 47 | (ret))) 48 | " 49 | let result = 50 | { Name = "Stub" 51 | Units = [Method { mainMethodDefinition with 52 | Body = [ Simple Ret ] } ] } 53 | checkPreparationResult source [result] 54 | 55 | [] 56 | let ``Hello world assembly should be processed`` () = 57 | let source = "(.assembly Hello 58 | (.method Main () System.Void (.entrypoint) 59 | (ldstr \"Hello, world!\") 60 | (call (mscorlib System.Console WriteLine (System.String) System.Void)) 61 | (ret))) 62 | " 63 | let result = 64 | { Name = "Hello" 65 | Units = [Method { Metadata = Set.singleton EntryPoint 66 | Visibility = Public 67 | Name = "Main" 68 | ArgumentTypes = List.empty 69 | ReturnType = typeof 70 | Body = [ Ldstr "Hello, world!" 71 | Call consoleWriteLine 72 | Simple Ret ] } ] } 73 | checkPreparationResult source [result] 74 | -------------------------------------------------------------------------------- /Naggum.Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Naggum.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test programs", "Test programs", "{2972575D-A421-4759-AE89-57560E0224AE}" 7 | ProjectSection(SolutionItems) = preProject 8 | tests\comment.naggum = tests\comment.naggum 9 | tests\comment.result = tests\comment.result 10 | tests\let-funcall.naggum = tests\let-funcall.naggum 11 | tests\let-funcall.result = tests\let-funcall.result 12 | tests\test.naggum = tests\test.naggum 13 | tests\test.result = tests\test.result 14 | EndProjectSection 15 | EndProject 16 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Naggum.Compiler", "Naggum.Compiler\Naggum.Compiler.fsproj", "{A4269C5E-E4AC-44BF-A06E-1B45248910AD}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Naggum.Runtime", "Naggum.Runtime\Naggum.Runtime.csproj", "{402B5E79-E063-4833-AE4B-2986AEEC1D75}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Naggum.Interactive", "Naggum.Interactive\Naggum.Interactive.csproj", "{34E781DF-EAD9-4034-ADE4-8DA41A16644A}" 21 | ProjectSection(ProjectDependencies) = postProject 22 | {402B5E79-E063-4833-AE4B-2986AEEC1D75} = {402B5E79-E063-4833-AE4B-2986AEEC1D75} 23 | EndProjectSection 24 | EndProject 25 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Naggum.Test", "Naggum.Test\Naggum.Test.fsproj", "{38230CBD-DE3E-4470-925F-DE966E8691AA}" 26 | EndProject 27 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution items", "Solution items", "{F85A1ACB-4A60-46A9-856D-92A3C9B97402}" 28 | ProjectSection(SolutionItems) = preProject 29 | .editorconfig = .editorconfig 30 | .travis.yml = .travis.yml 31 | appveyor.yml = appveyor.yml 32 | License.md = License.md 33 | Readme.md = Readme.md 34 | EndProjectSection 35 | EndProject 36 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Naggum.Assembler", "Naggum.Assembler\Naggum.Assembler.fsproj", "{40B84F1E-1823-4255-80D4-1297613025C1}" 37 | EndProject 38 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Naggum.Backend", "Naggum.Backend\Naggum.Backend.fsproj", "{243738F3-D798-4B09-8797-F90B21414B60}" 39 | EndProject 40 | Global 41 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 42 | Debug|Any CPU = Debug|Any CPU 43 | Release|Any CPU = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 46 | {A4269C5E-E4AC-44BF-A06E-1B45248910AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {A4269C5E-E4AC-44BF-A06E-1B45248910AD}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {A4269C5E-E4AC-44BF-A06E-1B45248910AD}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {A4269C5E-E4AC-44BF-A06E-1B45248910AD}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {402B5E79-E063-4833-AE4B-2986AEEC1D75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {402B5E79-E063-4833-AE4B-2986AEEC1D75}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {402B5E79-E063-4833-AE4B-2986AEEC1D75}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {402B5E79-E063-4833-AE4B-2986AEEC1D75}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {34E781DF-EAD9-4034-ADE4-8DA41A16644A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {34E781DF-EAD9-4034-ADE4-8DA41A16644A}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {34E781DF-EAD9-4034-ADE4-8DA41A16644A}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {34E781DF-EAD9-4034-ADE4-8DA41A16644A}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {38230CBD-DE3E-4470-925F-DE966E8691AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {38230CBD-DE3E-4470-925F-DE966E8691AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {38230CBD-DE3E-4470-925F-DE966E8691AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {38230CBD-DE3E-4470-925F-DE966E8691AA}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {40B84F1E-1823-4255-80D4-1297613025C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {40B84F1E-1823-4255-80D4-1297613025C1}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {40B84F1E-1823-4255-80D4-1297613025C1}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {40B84F1E-1823-4255-80D4-1297613025C1}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {243738F3-D798-4B09-8797-F90B21414B60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {243738F3-D798-4B09-8797-F90B21414B60}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {243738F3-D798-4B09-8797-F90B21414B60}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {243738F3-D798-4B09-8797-F90B21414B60}.Release|Any CPU.Build.0 = Release|Any CPU 70 | EndGlobalSection 71 | GlobalSection(SolutionProperties) = preSolution 72 | HideSolutionNode = FALSE 73 | EndGlobalSection 74 | EndGlobal 75 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Naggum [![Status Enfer][status-enfer]][andivionian-status-classifier] [![Build status][build-status-appveyor]][appveyor] [![Build Status][build-status-travis]][travis] 2 | ====== 3 | Naggum (named in honor of [Erik Naggum][eric-naggum]) /(is|will be)/ a Lisp 4 | dialect based on Common Language Infrastructure (aka .NET Framework / Mono). 5 | 6 | ## Disclaimer 7 | 8 | Naggum does not aim to be yet another Common Lisp or Scheme or Emacs Lisp or 9 | whatever implementation. Instead, we are trying to deliver a modern Lisp dialect 10 | that makes use of most of .Net's benefits. 11 | 12 | ## Documentation 13 | 14 | The documentation is available on [ReadTheDocs][read-the-docs]. 15 | 16 | ## License 17 | 18 | Naggum is licensed under the terms of MIT License. See License.md file for 19 | details. 20 | 21 | [andivionian-status-classifier]: https://github.com/ForNeVeR/andivionian-status-classifier 22 | [appveyor]: https://ci.appveyor.com/project/ForNeVeR/naggum/branch/develop 23 | [eric-naggum]: https://en.wikipedia.org/wiki/Erik_Naggum 24 | [read-the-docs]: http://naggum.readthedocs.org/ 25 | [travis]: https://travis-ci.org/codingteam/naggum 26 | 27 | [build-status-appveyor]: https://ci.appveyor.com/api/projects/status/ulgo3ry7eudc5d7a/branch/develop?svg=true 28 | [build-status-travis]: https://travis-ci.org/codingteam/naggum.svg?branch=develop 29 | [status-enfer]: https://img.shields.io/badge/status-enfer-orange.svg 30 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | before_build: 2 | - nuget restore 3 | 4 | version: 0.0.1.{build} 5 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | with import {}; rec { 2 | frameworkVersion = "4.5"; 3 | fsharpVersion = "4.0"; 4 | assemblyVersion = "4.4.0.0"; 5 | 6 | naggumEnv = stdenv.mkDerivation { 7 | name = "naggum"; 8 | buildInputs = [ mono fsharp dotnetPackages.Nuget ]; 9 | 10 | # For more info regarding this variable see: 11 | # https://github.com/NixOS/nixpkgs/blob/d4681bf62672083f92545e02e00b8cf040247e8d/pkgs/build-support/dotnetbuildhelpers/patch-fsharp-targets.sh 12 | FSharpTargetsPath="${fsharp}/lib/mono/${frameworkVersion}/Microsoft.FSharp.Targets"; 13 | 14 | # For more info regarding this variable see: 15 | # http://www.mono-project.com/docs/advanced/assemblies-and-the-gac/ 16 | MONO_PATH="${fsharp}/lib/mono/Reference Assemblies/Microsoft/FSharp/.NETFramework/v${fsharpVersion}/${assemblyVersion}/"; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Naggum.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Naggum.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Naggum" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Naggum" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /docs/about.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | Disclaimer 5 | ---------- 6 | 7 | Naggum doesn't aim to be yet another Common Lisp or Scheme or whatever 8 | implementation. Instead, we are trying to deliver a modern Lisp dialect that 9 | makes use of most of CLI benefits. 10 | 11 | Features 12 | -------- 13 | 14 | Naggum provides both direct access to low-level features of CLI, and allows its 15 | user to define types and functions just like any other high-level language. At 16 | the same time, it combines power of Lisp-inspired metaprogramming (macros) with 17 | strictness of strong-typed language. 18 | 19 | Contribute 20 | ---------- 21 | 22 | - Source Code: https://github.com/codingteam/naggum 23 | - Issue Tracker: https://github.com/codingteam/naggum/issues 24 | 25 | License 26 | ------- 27 | 28 | Naggum is licensed under the terms of MIT License. See `License.md`_ for 29 | details. 30 | 31 | .. _License.md: https://github.com/codingteam/naggum/blob/develop/License.md 32 | -------------------------------------------------------------------------------- /docs/build-guide.rst: -------------------------------------------------------------------------------- 1 | Build guide 2 | =========== 3 | 4 | To use Naggum, first of all you need to build it from source. 5 | 6 | Windows 7 | ------- 8 | 9 | To build Naggum on Windows, just use Visual Studio or MSBuild like that:: 10 | 11 | $ cd naggum 12 | $ nuget restore 13 | $ msbuild /p:Configuration=Release Naggum.sln 14 | 15 | Linux 16 | ----- 17 | 18 | See general build instructions for Linux in the file ``.travis.yml`` inside the 19 | Naggum source directory. 20 | 21 | You'll need `Mono`_, `NuGet`_ and `F# Compiler`_ installed. Some of them may or 22 | may not be part of your Mono installation; just make sure you've got them all. 23 | 24 | Please note that currently the project is compatible with Mono 4.4.2+. 25 | 26 | Below is an example of setting up these tools on `NixOS Linux`_; feel free to 27 | add instructions for any other distributions. 28 | 29 | NixOS Linux 30 | ^^^^^^^^^^^ 31 | 32 | *The instructions have been verified on NixOS 16.03. If something doesn't work, please file an issue.* 33 | 34 | Enter the development environment:: 35 | 36 | $ cd naggum 37 | $ nix-shell 38 | 39 | After that you can download the dependencies and build the project using 40 | ``xbuild``:: 41 | 42 | $ nuget restore 43 | $ xbuild /p:Configuration=Release /p:TargetFrameworkVersion="v4.5" 44 | 45 | After that, you can run ``Naggum.Compiler``, for example:: 46 | 47 | $ cd Naggum.Compiler/bin/Release/ 48 | $ mono Naggum.Compiler.exe ../../../tests/test.naggum 49 | $ mono test.exe 50 | 51 | Documentation 52 | ------------- 53 | 54 | You can build a local copy of Naggum documentation. To do that, install 55 | `Python`_ 2.7 and `Sphinx`_. Ensure that you have ``sphinx-build`` binary in 56 | your ``PATH`` or define ``SPHINXBUILD`` environment variable to choose an 57 | alternative Sphinx builder. After that go to `docs` directory and execute ``make 58 | html`` (on Linux) or ``.\make.bat html`` (on Windows). 59 | 60 | .. _F# Compiler: http://fsharp.org/ 61 | .. _Mono: http://www.mono-project.com/ 62 | .. _NixOS Linux: http://nixos.org/ 63 | .. _NuGet: http://www.nuget.org/ 64 | .. _Python: https://www.python.org/ 65 | .. _Sphinx: http://sphinx-doc.org/ 66 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Naggum documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Jan 08 15:42:43 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import shlex 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix(es) of source filenames. 38 | # You can specify multiple suffix as a list of string: 39 | # source_suffix = ['.rst', '.md'] 40 | source_suffix = '.rst' 41 | 42 | # The encoding of source files. 43 | #source_encoding = 'utf-8-sig' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = u'Naggum' 50 | copyright = u'2016, Naggum authors' 51 | author = u'Naggum authors' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = u'0.0.1' 59 | # The full version, including alpha/beta/rc tags. 60 | release = u'0.0.1' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = None 68 | 69 | # There are two options for replacing |today|: either, you set today to some 70 | # non-false value, then it is used: 71 | #today = '' 72 | # Else, today_fmt is used as the format for a strftime call. 73 | #today_fmt = '%B %d, %Y' 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | exclude_patterns = ['_build'] 78 | 79 | # The reST default role (used for this markup: `text`) to use for all 80 | # documents. 81 | #default_role = None 82 | 83 | # If true, '()' will be appended to :func: etc. cross-reference text. 84 | #add_function_parentheses = True 85 | 86 | # If true, the current module name will be prepended to all description 87 | # unit titles (such as .. function::). 88 | #add_module_names = True 89 | 90 | # If true, sectionauthor and moduleauthor directives will be shown in the 91 | # output. They are ignored by default. 92 | #show_authors = False 93 | 94 | # The name of the Pygments (syntax highlighting) style to use. 95 | pygments_style = 'sphinx' 96 | 97 | # A list of ignored prefixes for module index sorting. 98 | #modindex_common_prefix = [] 99 | 100 | # If true, keep warnings as "system message" paragraphs in the built documents. 101 | #keep_warnings = False 102 | 103 | # If true, `todo` and `todoList` produce output, else they produce nothing. 104 | todo_include_todos = False 105 | 106 | 107 | # -- Options for HTML output ---------------------------------------------- 108 | 109 | # The theme to use for HTML and HTML Help pages. See the documentation for 110 | # a list of builtin themes. 111 | html_theme = 'default' 112 | 113 | # Theme options are theme-specific and customize the look and feel of a theme 114 | # further. For a list of options available for each theme, see the 115 | # documentation. 116 | #html_theme_options = {} 117 | 118 | # Add any paths that contain custom themes here, relative to this directory. 119 | #html_theme_path = [] 120 | 121 | # The name for this set of Sphinx documents. If None, it defaults to 122 | # " v documentation". 123 | #html_title = None 124 | 125 | # A shorter title for the navigation bar. Default is the same as html_title. 126 | #html_short_title = None 127 | 128 | # The name of an image file (relative to this directory) to place at the top 129 | # of the sidebar. 130 | #html_logo = None 131 | 132 | # The name of an image file (within the static path) to use as favicon of the 133 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 134 | # pixels large. 135 | #html_favicon = None 136 | 137 | # Add any paths that contain custom static files (such as style sheets) here, 138 | # relative to this directory. They are copied after the builtin static files, 139 | # so a file named "default.css" will overwrite the builtin "default.css". 140 | html_static_path = ['_static'] 141 | 142 | # Add any extra paths that contain custom files (such as robots.txt or 143 | # .htaccess) here, relative to this directory. These files are copied 144 | # directly to the root of the documentation. 145 | #html_extra_path = [] 146 | 147 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 148 | # using the given strftime format. 149 | #html_last_updated_fmt = '%b %d, %Y' 150 | 151 | # If true, SmartyPants will be used to convert quotes and dashes to 152 | # typographically correct entities. 153 | #html_use_smartypants = True 154 | 155 | # Custom sidebar templates, maps document names to template names. 156 | #html_sidebars = {} 157 | 158 | # Additional templates that should be rendered to pages, maps page names to 159 | # template names. 160 | #html_additional_pages = {} 161 | 162 | # If false, no module index is generated. 163 | #html_domain_indices = True 164 | 165 | # If false, no index is generated. 166 | #html_use_index = True 167 | 168 | # If true, the index is split into individual pages for each letter. 169 | #html_split_index = False 170 | 171 | # If true, links to the reST sources are added to the pages. 172 | #html_show_sourcelink = True 173 | 174 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 175 | #html_show_sphinx = True 176 | 177 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 178 | #html_show_copyright = True 179 | 180 | # If true, an OpenSearch description file will be output, and all pages will 181 | # contain a tag referring to it. The value of this option must be the 182 | # base URL from which the finished HTML is served. 183 | #html_use_opensearch = '' 184 | 185 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 186 | #html_file_suffix = None 187 | 188 | # Language to be used for generating the HTML full-text search index. 189 | # Sphinx supports the following languages: 190 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 191 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 192 | #html_search_language = 'en' 193 | 194 | # A dictionary with options for the search language support, empty by default. 195 | # Now only 'ja' uses this config value 196 | #html_search_options = {'type': 'default'} 197 | 198 | # The name of a javascript file (relative to the configuration directory) that 199 | # implements a search results scorer. If empty, the default will be used. 200 | #html_search_scorer = 'scorer.js' 201 | 202 | # Output file base name for HTML help builder. 203 | htmlhelp_basename = 'Naggumdoc' 204 | 205 | # -- Options for LaTeX output --------------------------------------------- 206 | 207 | latex_elements = { 208 | # The paper size ('letterpaper' or 'a4paper'). 209 | #'papersize': 'letterpaper', 210 | 211 | # The font size ('10pt', '11pt' or '12pt'). 212 | #'pointsize': '10pt', 213 | 214 | # Additional stuff for the LaTeX preamble. 215 | #'preamble': '', 216 | 217 | # Latex figure (float) alignment 218 | #'figure_align': 'htbp', 219 | } 220 | 221 | # Grouping the document tree into LaTeX files. List of tuples 222 | # (source start file, target name, title, 223 | # author, documentclass [howto, manual, or own class]). 224 | latex_documents = [ 225 | (master_doc, 'Naggum.tex', u'Naggum Documentation', 226 | u'Naggum authors', 'manual'), 227 | ] 228 | 229 | # The name of an image file (relative to this directory) to place at the top of 230 | # the title page. 231 | #latex_logo = None 232 | 233 | # For "manual" documents, if this is true, then toplevel headings are parts, 234 | # not chapters. 235 | #latex_use_parts = False 236 | 237 | # If true, show page references after internal links. 238 | #latex_show_pagerefs = False 239 | 240 | # If true, show URL addresses after external links. 241 | #latex_show_urls = False 242 | 243 | # Documents to append as an appendix to all manuals. 244 | #latex_appendices = [] 245 | 246 | # If false, no module index is generated. 247 | #latex_domain_indices = True 248 | 249 | 250 | # -- Options for manual page output --------------------------------------- 251 | 252 | # One entry per manual page. List of tuples 253 | # (source start file, name, description, authors, manual section). 254 | man_pages = [ 255 | (master_doc, 'naggum', u'Naggum Documentation', 256 | [author], 1) 257 | ] 258 | 259 | # If true, show URL addresses after external links. 260 | #man_show_urls = False 261 | 262 | 263 | # -- Options for Texinfo output ------------------------------------------- 264 | 265 | # Grouping the document tree into Texinfo files. List of tuples 266 | # (source start file, target name, title, author, 267 | # dir menu entry, description, category) 268 | texinfo_documents = [ 269 | (master_doc, 'Naggum', u'Naggum Documentation', 270 | author, 'Naggum', 'One line description of project.', 271 | 'Miscellaneous'), 272 | ] 273 | 274 | # Documents to append as an appendix to all manuals. 275 | #texinfo_appendices = [] 276 | 277 | # If false, no module index is generated. 278 | #texinfo_domain_indices = True 279 | 280 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 281 | #texinfo_show_urls = 'footnote' 282 | 283 | # If true, do not generate a @detailmenu in the "Top" node's menu. 284 | #texinfo_no_detailmenu = False 285 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Naggum documentation 2 | ==================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | about 8 | build-guide 9 | usage 10 | specification 11 | 12 | Naggum (named in honor of `Erik Naggum`_) is a modern statically typed Lisp 13 | variant that is targeting `Common Language Infrastructure`_ (CLI) runtime 14 | system. 15 | 16 | .. _Common Language Infrastructure: http://www.ecma-international.org/publications/standards/Ecma-335.htm 17 | .. _Erik Naggum: https://en.wikipedia.org/wiki/Erik_Naggum 18 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 1>NUL 2>NUL 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Naggum.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Naggum.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /docs/specification.rst: -------------------------------------------------------------------------------- 1 | Naggum Specification 2 | ==================== 3 | 4 | Features 5 | -------- 6 | 7 | - based on CLR; 8 | - Lisp-2; 9 | - compiles to CIL assemblies; 10 | - is not a Common Lisp implementation; 11 | - seamlessly interoperates with other CLR code. 12 | 13 | Language 14 | -------- 15 | 16 | Special forms 17 | ~~~~~~~~~~~~~ 18 | 19 | 1. ``(let (bindings*) body*)`` where ``bindings`` follow a pattern of 20 | ``(name initial-value)`` creates a lexical scope, evaluates initial 21 | values, binds them to corresponding names and evaluates the body, 22 | returning the value of last expression. Naggum’s ``let`` is a loner: 23 | every one is inherently iterative (like ``let*``) and recursive 24 | (like ``let rec``). 25 | 26 | 2. ``(defun name (parms*) body*)`` defines a function (internally it 27 | will be a public static method). Naggum is a Lisp-2, henceforth a 28 | function and a variable can share their names. 29 | 30 | 3. ``(if condition if-true [if-false])`` evaluates given ``condition``. 31 | If it is true (as in “not null, not zero, not false”) it evaluates 32 | ``if-true`` form and returns it’s result. If ``condition`` evaluates 33 | to false, null or zero then ``if-false`` form (if given) is 34 | evaluated and it’s result (or null, if no ``if-false`` form is 35 | given) is returned from ``if``. 36 | 37 | 4. ``(fun-name args*)`` applies function named ``fun-name`` to given 38 | arguments. 39 | 40 | 5. ``(new type-name args*)`` calls applicable constructor of type named 41 | ``type-name`` with given arguments and returns created object. 42 | ``(new (type-name generic-args*) args*)`` ``new`` form calls 43 | applicable constructor of generic type named ``type-name``, assuming 44 | generic parameters in ``generic-args`` and with given arguments and 45 | returns created object. 46 | 47 | 6. ``(call method-name object-var args*)`` Performs virtual call of 48 | method named ``method-name`` on object referenced by ``object-var`` 49 | with given arguments. 50 | 51 | 7. ``(lambda (parms*) body*)`` Constructs anonymous function with 52 | ``parms`` as parameters and ``body`` as body and returns it as a 53 | result. 54 | 55 | 8. ``(eval form [environment])`` evaluates form using supplied lexical 56 | environment. If no environment is given, uses current one. 57 | 58 | 9. ``(error error-type args*)`` throws an exception of ``error-type``, 59 | constructed with ``args``. 60 | 61 | 10. ``(try form (catch-forms*))`` where ``catch-forms`` follow a pattern 62 | of ``(error-type handle-form)`` tries to evaluate ``form``. If any 63 | error is encountered, evaluates ``handle-form`` with the most 64 | appropriate ``error-type``. 65 | 66 | 11. ``(defmacro name (args*))`` defines a macro that will be expanded at 67 | compile time. 68 | 69 | 12. ``(require namespaces*)`` states that ``namespaces`` should be used 70 | to search for symbols. 71 | 72 | 13. ``(cond (cond-clauses*))`` where ``cond-clauses`` follow a pattern 73 | of ``(condition form)`` sequentially evaluates conditions, until one 74 | of them is evaluated to ``true``, non-null or non-zero value, then 75 | the corresponding ``form`` is evaluated and it’s result returned. 76 | 77 | 14. ``(set var value)`` sets the value of ``var`` to ``value``. ``var`` 78 | can be a local variable, function parameter or a field of some 79 | object. 80 | 81 | Quoting 82 | ~~~~~~~ 83 | 84 | 1. ``(quote form)`` indicates simple quoting. ``form`` is returned 85 | as-is. 86 | 87 | 2. ``(quasi-quote form)`` returns ``form`` with ``unquote`` and 88 | ``splice-unquote`` expressions inside evaluated and substituted with 89 | their results accordingly 90 | 91 | 3. ``(unquote form)`` if encountered in ``quasi-quote`` form, will be 92 | substituted by a result of ``form`` evaluation 93 | 94 | 4. ``(splice-unquote form)`` same as ``unquote``, but if ``form`` 95 | evaluation result is a list, then it’s elements will be spliced as an 96 | elements of the containing ``list``. 97 | 98 | Type declaration forms 99 | ~~~~~~~~~~~~~~~~~~~~~~ 100 | 101 | - ``(deftype type-name ([parent-types*]) members*)`` Defines CLR type, 102 | inheriting from ``parent-types`` with defined members. 103 | 104 | - ``(deftype (type-name generic-parms*) ([parent-types*]) members*)`` 105 | Defines generic CLR type, polymorphic by ``generic-parms``, 106 | inheriting from ``parent-types`` with defined members. 107 | 108 | - ``(definterface type-name ([parent-types*]) members*)`` Defines CLR 109 | interface type, inheriting from ``parent-types`` with defined 110 | members. 111 | 112 | - ``(definterface (type-name generic-parms*) ([parent-types*]) members*)`` 113 | Defines generic CLR interface type, polymorphic by ``generic-parms``, 114 | inheriting from ``parent-types`` with defined members. 115 | 116 | If no ``parent-types`` is supplied, ``System.Object`` is assumed. 117 | 118 | Member declaration forms 119 | ~~~~~~~~~~~~~~~~~~~~~~~~ 120 | 121 | - ``(field [access-type] field-name)`` declares a field with name given 122 | by ``field-name`` and access permissions defined by ``access-type``. 123 | - ``(method [access-type] method-name (parms*) body*)`` declares an 124 | instance method. Otherwise identical to ``defun``. 125 | 126 | Available values for ``access-type`` are ``public``\ (available to 127 | everybody), ``internal``\ (available to types that inherit from this 128 | type) and ``private``\ (available only to methods in this type). If no 129 | ``access-type`` is given, ``private`` is assumed. 130 | 131 | Standard library 132 | ---------------- 133 | 134 | Naggum is designed to use CLR standard libraries, but some types and 135 | routines are provided to facilitate lisp-style programming. 136 | 137 | Cons 138 | ~~~~ 139 | 140 | Cons-cell is the most basic building block of complex data structures. 141 | It contains exactly two objects of any types, referenced as *CAR* (left 142 | part, head) and *CDR* (right part, tail) 143 | 144 | Symbol 145 | ~~~~~~ 146 | 147 | Symbol is a type that represents language primitives like variable, 148 | function and type names. 149 | 150 | Naggum Reader 151 | ~~~~~~~~~~~~~ 152 | 153 | Reader reads Lisp objects from any input stream, returning them as lists 154 | and atoms. 155 | 156 | Naggum Writer 157 | ~~~~~~~~~~~~~ 158 | 159 | Writer writes Lisp objects to any output stream, performing output 160 | formatting if needed. 161 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | Naggum usage 2 | ============ 3 | 4 | Currently there are two dialects of Naggum: high-level *Compiler* and low-level 5 | *Assembler*. 6 | 7 | Naggum Compiler 8 | --------------- 9 | 10 | Command line syntax for Naggum Compiler is:: 11 | 12 | $ Naggum.Compiler source.naggum... [/r:assembly]... 13 | 14 | Each input source file will be compiled to a separate executable assembly (i.e. 15 | an ``.exe`` file) in the current directory. You can also pass a list of files to 16 | be referenced by these assemblies. 17 | 18 | ``.naggum`` extension is recommended for high-level Naggum files. 19 | 20 | Naggum Assembler 21 | ---------------- 22 | 23 | Naggum Assembler uses low-level Naggum dialect. Command line syntax is:: 24 | 25 | $ Naggum.Assembler source.nga... 26 | 27 | Each input file may contain zero or more assembly constructs. Every assembly 28 | will be saved to its own executable file in the current directory. 29 | 30 | ``.nga`` extension is recommended for low-level Naggum files. 31 | 32 | S-expression syntax 33 | ------------------- 34 | 35 | Each Naggum program (either high-level or low-level) is written as a sequence of 36 | S-expression forms. In s-expression, everything is either an atom or a list. 37 | Atoms are written as-is, lists should be taken into parens. 38 | 39 | Possible atom values are:: 40 | 41 | "A string" 42 | 1.4e-5 ; a number 43 | System.Console ; a symbol 44 | 45 | A symbol is a sequence of letters, digits, and any of the following characters: 46 | ``+-*/=<>!?.``. 47 | 48 | Lists are simply sequences of s-expressions in parens:: 49 | 50 | (this is a list) 51 | 52 | (this (is ("Also") a.list)) 53 | 54 | Naggum source code may also include comments. Everything after ``;`` character 55 | will be ignored till the end of the line:: 56 | 57 | (valid atom) ; this is a comment 58 | 59 | Low-level syntax 60 | ---------------- 61 | 62 | Naggum low-level syntax is closer to `CIL`_. It may be used to define CLI 63 | constructs such as assemblies, modules, types and methods. Every ``.nga`` file 64 | may contain zero or more assembly definitions. 65 | 66 | Assembly definition 67 | ^^^^^^^^^^^^^^^^^^^ 68 | 69 | Assembly defitinion should have the following form:: 70 | 71 | (.assembly Name 72 | Item1 73 | Item2 74 | ...) 75 | 76 | Assembly items can be methods and types. Top level methods defined in an 77 | ``.assembly`` form will be compiled to global CIL functions. 78 | 79 | Type definitions are not supported yet. 80 | 81 | Each assembly may contain one entry point method (either a static type method or 82 | an assembly global function marked by ``.entrypoint`` property). 83 | 84 | Method definition 85 | ^^^^^^^^^^^^^^^^^ 86 | 87 | Method definition should have the following form:: 88 | 89 | (.method Name (argument types) return-type (metadata items) 90 | body-statements 91 | ...) 92 | 93 | Method argument and return types should be fully-qualified (e.g. must include a 94 | namespace: for example, ``System.Void``). 95 | 96 | The only supported metadata item is ``.entrypoint``. It marks a method as an 97 | assembly entry point. 98 | 99 | Method example:: 100 | 101 | (.method Main () System.Void (.entrypoint) 102 | (ldstr "Hello, world!") 103 | (call (mscorlib System.Console WriteLine (System.String) System.Void)) 104 | (ret)) 105 | 106 | Method body should be a CIL instruction sequence. 107 | 108 | CIL instructions 109 | ^^^^^^^^^^^^^^^^ 110 | 111 | Currently only a small subset of all available CIL instructions is supported by 112 | Naggum. This set will be extended in future. 113 | 114 | #. Call instruction:: 115 | 116 | (call (assembly type-name method-name (argument types) return-type)) 117 | 118 | Currently assembly name is ignored; only ``mscorlib`` methods can be called. 119 | Static assembly function calls are not supported yet. 120 | 121 | Method argument and return types should be fully-qualified. 122 | 123 | #. Load string instruction:: 124 | 125 | (ldstr "Hello, world") 126 | 127 | Loads a string onto a CLI stack. 128 | 129 | #. Return instruction:: 130 | 131 | (ret) 132 | 133 | Return from current method. 134 | 135 | Example assembly definition 136 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 137 | 138 | :: 139 | 140 | (.assembly Hello 141 | (.method Main () System.Void (.entrypoint) 142 | (ldstr "Hello, world!") 143 | (call (mscorlib System.Console WriteLine (System.String) System.Void)) 144 | (ret))) 145 | 146 | High-level syntax 147 | ----------------- 148 | 149 | Every high-level Naggum program is a sequence of function definitions and a 150 | top-level executable statements. Functions defined in an assembly are also 151 | available as public static methods to be called by external assemblies. 152 | 153 | Functions are defined using ``defun`` special form:: 154 | 155 | (defun function-name (arg1 arg2) 156 | statement1 157 | statement2) 158 | 159 | For example:: 160 | 161 | (defun println (arg) 162 | (System.Console.WriteLine arg)) 163 | 164 | Naggum is a Lisp-2, henceforth a function and a variable can share their names. 165 | 166 | Currently executable statements may be one of the following. 167 | 168 | #. Let bindings:: 169 | 170 | (let ((variable-name expression) 171 | (variable-name-2 expression-2)) 172 | body 173 | statements) 174 | 175 | Creates a lexical scope, evaluates initial values, binds them to corresponding 176 | names and evaluates the body, returning the value of last expression. 177 | 178 | Naggum's ``let`` is a loner: every one is inherently iterative (like ``let*``) 179 | and recursive (like `let rec`). 180 | 181 | #. Arithmetic statements:: 182 | 183 | (+ 2 2) 184 | 185 | #. Function calls:: 186 | 187 | (defun func () (+ 2 2)) 188 | 189 | (func) 190 | 191 | #. Static CLI method calls:: 192 | 193 | (System.Console.WriteLine "Math:") 194 | 195 | #. Conditional statements:: 196 | 197 | (if condition 198 | true-statement 199 | false-statement) 200 | 201 | If the ``condition`` is true (as in "not null, not zero, not false") it 202 | evaluates the ``true-statement`` form and returns its result. If the 203 | ``condition`` evaluates to false, null or zero, then the ``false-statement`` 204 | form is evaluated and its result is returned from ``if``. 205 | 206 | #. Reduced if statements:: 207 | 208 | (if condition 209 | true-statement) 210 | 211 | #. Constructor calls:: 212 | 213 | (new Naggum.Runtime.Cons "OK" "FAILURE") 214 | 215 | Calls an applicable constructor of a type named `Naggum.Runtime.Cons` with the 216 | given arguments and returns an object created. 217 | 218 | .. _CIL: https://en.wikipedia.org/wiki/Common_Intermediate_Language 219 | -------------------------------------------------------------------------------- /tests/comment.naggum: -------------------------------------------------------------------------------- 1 | ; a comment 2 | (System.Console.WriteLine "ok") 3 | ; a second comment 4 | ; a third comment -------------------------------------------------------------------------------- /tests/comment.result: -------------------------------------------------------------------------------- 1 | ok 2 | -------------------------------------------------------------------------------- /tests/let-funcall.naggum: -------------------------------------------------------------------------------- 1 | (defun test-let () 2 | (System.Console.Write "Let: ") 3 | (let ((ok "OK")) 4 | (System.Console.WriteLine ok))) 5 | (System.Console.WriteLine "Naggum test suite") 6 | 7 | (let ((thread (System.Threading.Thread.get_CurrentThread)) 8 | (culture (System.Globalization.CultureInfo.get_InvariantCulture))) 9 | (System.Console.WriteLine "Setting up an environment...") 10 | (call set_CurrentCulture thread culture)) 11 | 12 | (test-let) 13 | -------------------------------------------------------------------------------- /tests/let-funcall.result: -------------------------------------------------------------------------------- 1 | Naggum test suite 2 | Setting up an environment... 3 | Let: OK 4 | -------------------------------------------------------------------------------- /tests/test.naggum: -------------------------------------------------------------------------------- 1 | (defun test-funcall (test-arg) 2 | (System.Console.Write "Functions: ") 3 | (System.Console.WriteLine test-arg)) 4 | 5 | (defun test-conditionals () 6 | (System.Console.WriteLine "Conditionals:") 7 | (if 1 (System.Console.WriteLine "Reduced if: OK")) 8 | (if 1 9 | (System.Console.WriteLine "Full if (true branch): OK") 10 | (System.Console.WriteLine "Full if (true branch): FAILURE")) 11 | (if 0 12 | (System.Console.WriteLine "Full if (false branch): FAILURE") 13 | (System.Console.WriteLine "Full if (false branch): OK"))) 14 | 15 | (defun test-let () 16 | (System.Console.Write "Let: ") 17 | (let ((ok "OK")) 18 | (System.Console.WriteLine ok))) 19 | 20 | (defun test-quote () 21 | (System.Console.WriteLine "Quoting:") 22 | (System.Console.Write "Symbol: ") (System.Console.WriteLine (quote OK)) 23 | (System.Console.Write "List: ") (System.Console.WriteLine (quote (OK)))) 24 | 25 | (defun test-new () 26 | (System.Console.Write "Object construction: ") 27 | (let ((ok-sym (new Naggum.Runtime.Symbol "OK"))) 28 | (System.Console.WriteLine ok-sym))) 29 | 30 | (defun test-cons () 31 | (System.Console.WriteLine "Cons:") 32 | (let ((test-car (new Naggum.Runtime.Cons "OK" "FAILURE")) 33 | (test-cdr (new Naggum.Runtime.Cons "FAILURE" "OK"))) 34 | (System.Console.Write "CAR: ") (System.Console.WriteLine (Naggum.Runtime.Cons.Car test-car)) 35 | (System.Console.Write "CDR: ") (System.Console.WriteLine (Naggum.Runtime.Cons.Cdr test-cdr)))) 36 | 37 | (defun test-math () 38 | (System.Console.WriteLine "Math:") 39 | (System.Console.WriteLine "Integers:") 40 | (System.Console.Write "2+2=") (System.Console.WriteLine (+ 2 2)) 41 | (System.Console.Write "2-2=") (System.Console.WriteLine (- 2 2)) 42 | (System.Console.Write "2*3=") (System.Console.WriteLine (* 2 3)) 43 | (System.Console.Write "2/2=") (System.Console.WriteLine (/ 2 2)) 44 | 45 | (System.Console.WriteLine "Floats:") 46 | (System.Console.Write "2.0 + 0.5=") (System.Console.WriteLine (+ 2.0 0.5)) 47 | (System.Console.Write "3.0 - 1.5=") (System.Console.WriteLine (- 3.0 1.5)) 48 | (System.Console.Write "2.0 * 0.7=") (System.Console.WriteLine (* 2.0 0.7)) 49 | (System.Console.Write "3.0 / 2.0=") (System.Console.WriteLine (/ 3.0 2.0))) 50 | 51 | (defun test-instance-call () 52 | (System.Console.WriteLine "Instance calls:") 53 | (let ((test-obj (new System.Random 100500))) ; added for reproducibility 54 | (System.Console.Write "Random number:") 55 | (System.Console.WriteLine (call Next test-obj)))) 56 | 57 | (System.Console.WriteLine "Naggum test suite") 58 | 59 | (let ((thread (System.Threading.Thread.get_CurrentThread)) 60 | (culture (System.Globalization.CultureInfo.get_InvariantCulture))) 61 | (System.Console.WriteLine "Setting up an environment...") 62 | (call set_CurrentCulture thread culture)) 63 | 64 | (test-funcall "OK") 65 | (test-conditionals) 66 | (test-let) 67 | (test-quote) 68 | (test-new) 69 | (test-cons) 70 | (test-instance-call) 71 | (test-math) 72 | -------------------------------------------------------------------------------- /tests/test.result: -------------------------------------------------------------------------------- 1 | Naggum test suite 2 | Setting up an environment... 3 | Functions: OK 4 | Conditionals: 5 | Reduced if: OK 6 | Full if (true branch): OK 7 | Full if (false branch): OK 8 | Let: OK 9 | Quoting: 10 | Symbol: OK 11 | List: (OK) 12 | Object construction: OK 13 | Cons: 14 | CAR: OK 15 | CDR: OK 16 | Instance calls: 17 | Random number:1010002958 18 | Math: 19 | Integers: 20 | 2+2=4 21 | 2-2=0 22 | 2*3=6 23 | 2/2=1 24 | Floats: 25 | 2.0 + 0.5=2.5 26 | 3.0 - 1.5=1.5 27 | 2.0 * 0.7=1.4 28 | 3.0 / 2.0=1.5 29 | --------------------------------------------------------------------------------