diff --git a/Ftests/Input5.fk b/Ftests/Input5.fk new file mode 100644 index 0000000..e071083 --- /dev/null +++ b/Ftests/Input5.fk @@ -0,0 +1,10 @@ +#include "funkylib.fk" + +var str: u8[] = "Very Strong string really difficult to break and very long as you can see"; + +putnbr(strlen(str)); +write 10; +putstr(str); +write 10; +putstrrev(str); +write 10; diff --git a/Ftests/Output5 b/Ftests/Output5 new file mode 100644 index 0000000..9e121f5 --- /dev/null +++ b/Ftests/Output5 @@ -0,0 +1,3 @@ +73 +Very Strong string really difficult to break and very long as you can see +ees nac uoy sa gnol yrev dna kaerb ot tluciffid yllaer gnirts gnortS yreV diff --git a/Ftests/brainFunk.fk b/Ftests/brainFunk.fk new file mode 100644 index 0000000..5bbbc78 --- /dev/null +++ b/Ftests/brainFunk.fk @@ -0,0 +1,105 @@ +#include "funkylib.fk" + +#define OP_END 0 +#define OP_INC_DP 1 +#define OP_DEC_DP 2 +#define OP_INC_VAL 3 +#define OP_DEC_VAL 4 +#define OP_OUT 5 +#define OP_INS 6 +#define OP_JMP_FWD 7 +#define OP_JMP_BCK 8 + +#define SUCCESS 0 +#define FAILURE 1 + +#define PROGRAM_SIZE 4096 +#define STACK_SIZE 512 +#define DATA_SIZE 4096 + +allocate PROGRAM: u16 = PROGRAM_SIZE * 2; +allocate STACK: u16 = STACK_SIZE; +var SP: u32 = 0; + + + +var data1: u64 = _argv[1]; +var pc: u16 = 0; +var jmp_pc: u16 = 0; +var i: i32 = 0; +while (data1[i] != 0) { + if (data1[i] == 62) { + PROGRAM[pc * 2] = OP_INC_DP; + } + if (data1[i] == 60) { + PROGRAM[pc * 2] = OP_DEC_DP; + } + if (data1[i] == 43) { + PROGRAM[pc * 2] = OP_INC_VAL; + } + if (data1[i] == 45) { + PROGRAM[pc * 2] = OP_DEC_VAL; + } + if (data1[i] == 46) { + PROGRAM[pc * 2] = OP_OUT; + } + if (data1[i] == 44) { + PROGRAM[pc * 2] = OP_INS; + } + if (data1[i] == 91) { + PROGRAM[pc * 2] = OP_JMP_FWD; + STACK[SP] = pc; + SP = SP + 1; + } + if (data1[i] == 93) { + SP = SP - 1; + jmp_pc = STACK[SP]; + PROGRAM[pc * 2] = OP_JMP_BCK; + PROGRAM[pc * 2 + 1] = jmp_pc; + PROGRAM[jmp_pc * 2 + 1] = pc; + } + i = i + 1; + pc = pc + 1; +} + +PROGRAM[pc * 2] = OP_END; + +pc = 0; +allocate data2: u64 = DATA_SIZE; +var ptr: u32 = DATA_SIZE; +while (ptr != 0) { + data2[ptr] = 0; + ptr = ptr - 1; +} +while (PROGRAM[pc * 2] != OP_END) { + if (PROGRAM[pc * 2] == OP_INC_DP) { + ptr = ptr + 1; + } + if (PROGRAM[pc * 2] == OP_DEC_DP) { + ptr = ptr - 1; + } + if (PROGRAM[pc * 2] == OP_INC_VAL) { + data2[ptr] = data2[ptr] + 1; + } + if (PROGRAM[pc * 2] == OP_DEC_VAL) { + data2[ptr] = data2[ptr] - 1; + } + if (PROGRAM[pc * 2] == OP_OUT) { + write data2[ptr]; + } + if (PROGRAM[pc * 2] == OP_INS) { + data2[ptr] = 0; + } + if (PROGRAM[pc * 2] == OP_JMP_FWD) { + if (data2[ptr] == 0) { + pc = PROGRAM[pc * 2 + 1]; + } + } + if (PROGRAM[pc * 2] == OP_JMP_BCK) { + if (data2[ptr] != 0) { + pc = PROGRAM[pc * 2 + 1]; + } + } + pc = pc + 1; +} +return 0; \ No newline at end of file diff --git a/Ftests/funkylib.fk b/Ftests/funkylib.fk new file mode 100644 index 0000000..b0c8a92 --- /dev/null +++ b/Ftests/funkylib.fk @@ -0,0 +1,69 @@ +funk strlen(str: u64): i32 { + var i: i32 = 0; + while (str[i] != 0) { + i = i + 1; + } + return i; +} + +funk putstr(str: u64): i32 { + var size: i32 = strlen(str); + var i: i32 = 0; + while (i < size) { + write str[i]; + i = i + 1; + } + return 0; +} + +funk putnbr(nb: i32): i32 { + var pow: i32 = 1; + var save: i32 = nb; + + if (nb == 0) { + write 48; + return 0; + } + + if (nb < 0) { + write 45; + nb = nb * -1; + } + + while (save > 0) { + pow = pow * 10; + save = save / 10; + } + pow = pow / 10; + + while (pow > 0) { + write ((nb / pow) + 48); + nb = nb % pow; + pow = pow / 10; + } + return 0; +} + +funk putstrrev(str: u64): i32 { + var size: i32 = strlen(str); + var i: i32 = size - 1; + while (i >= 0) { + write str[i]; + i = i - 1; + } + return 0; +} + +funk strcmp(str1: u64, str2: u64): i32 { + var i: i32 = 0; + while (str1[i] != 0 && str2[i] != 0) { + if (str1[i] != str2[i]) { + return 1; + } + i = i + 1; + } + if (str1[i] != str2[i]) { + return 1; + } + return 0; +} diff --git a/brainFunk.fk b/brainFunk.fk index 5bbbc78..bf0445e 100644 --- a/brainFunk.fk +++ b/brainFunk.fk @@ -71,6 +71,7 @@ while (ptr != 0) { data2[ptr] = 0; ptr = ptr - 1; } +var tmp: u8 = 0; while (PROGRAM[pc * 2] != OP_END) { if (PROGRAM[pc * 2] == OP_INC_DP) { ptr = ptr + 1; @@ -88,7 +89,8 @@ while (PROGRAM[pc * 2] != OP_END) { write data2[ptr]; } if (PROGRAM[pc * 2] == OP_INS) { - data2[ptr] = 0; + read tmp; + data2[ptr] = tmp; } if (PROGRAM[pc * 2] == OP_JMP_FWD) { if (data2[ptr] == 0) { diff --git a/documentation/docs/Technical Documentation/Funk Language/Bnf.md b/documentation/docs/Technical Documentation/Funk Language/Bnf.md index 1f538ae..0b91d64 100644 --- a/documentation/docs/Technical Documentation/Funk Language/Bnf.md +++ b/documentation/docs/Technical Documentation/Funk Language/Bnf.md @@ -1,17 +1,15 @@ -# Glados - #Grammar Rules 1.Program Structure -```bnf +```bnf ::= { }* ``` 2.Declarations: -```bnf +```bnf ::= | | - ``` +``` 3.Function Definition: ```bnf @@ -24,7 +22,7 @@ ``` 5.Parameter: -```bnf +```bnf ::= ":" ``` @@ -39,12 +37,12 @@ ``` 8.Expressions: -```bnf +```bnf ::= | | | | "(" ")" ``` 9.Binary Operation: -```bnf +```bnf ::= ``` diff --git a/documentation/docs/User Documentation/Funk_Introduction_cn.md b/documentation/docs/User Documentation/Funk_Introduction_cn.md new file mode 100644 index 0000000..36681c8 --- /dev/null +++ b/documentation/docs/User Documentation/Funk_Introduction_cn.md @@ -0,0 +1,64 @@ +## The Funk编程语言 + +欢迎来到 *The Funk Programming Language*,这是有关Funk的介绍。Funk是一种受Rust和C传统启发的高级和低级控制语言。Funk注重现代语言的优雅语法,同时提供对代码的控制。 + +## Funk适用于谁? + +Funk适用于那些程序员,用一个词来形容就是“时髦的”。 +在Funk基金会,我们希望有一种语言,能让来自各行各业和背景的人都能拥抱他们的真我。成为Funk程序员不仅仅是简历上的一项技能,更是一种精神 + +## 法律事项 + +Funk是一种开源语言,欢迎新的贡献者。写Funk代码的唯一要求是要时髦。 + +## 入门指南 + +让我们开始你的Funk之旅吧!有很多东西要学,但每个旅程都要从某个地方开始。我们将讨论在Funk中编写*Hello World!*程序以及如何使用语法。 + +## Hello World + +Hello World!这是经典的、每次学习新东西时都会写的第一个程序。在Funk中我们也会做同样的事情! + +首先,创建一个新的项目文件夹和一个名为 `hello.fk` 的文件。Funk使用 `.fk` 扩展名来表示其源文件。 + +通过使用 `var` 关键字创建一个变量。给它一个名字,然后使用 `:` 符号指定类型。Funk有多种原始类型,我们将在后面的章节中介绍,现在我们想要存储一个字符串。我们将使用 `u8[]` 来存储我们的字符串。然后使用 = 符号添加一个值。在这里指定我们的消息。用必需的 `;` 分号结束你的指令,这样你就创建了你的第一个变量! + +```ts +var hello: u8[] = "Hello World!"; +``` + +现在,为了打印它,我们将使用Funk的标准库:`funkystd`。 + +在你的文件顶部添加以下内容 `"#include "funkystd"`。这将为你的程序提供比以往更多的功能实用性!现在我们可以使用库中的 `putstr` 函数来打印我们的消息。 + +```ts +#include "funkystd" + +var hello: u8[] = "Hello World!"; + +putstrln(hello); +``` + +恭喜!你刚刚在Funk中编写了你的第一个程序!但等一下,我们还没有运行它!不要担心,我们将看到如何在下一章中运行Funk程序。 + +## 运行Funk程序 + +你刚刚完成了你的第一个Funk *Hello World!*,但你仍然不知道如何运行它?在本章中,我们将介绍Funk的执行方式。 + +Funk是一种**编译**语言,这意味着它需要被放入另一个程序中才能执行。但别担心,我们已经为你准备好了,你不必自己编写。使用命令行界面,输入以下命令: + +```console +$ funkc hello.fk +Created: out.bin +``` + +你得到了一个新文件:`out.bin`。这个文件代表我们的程序的`字节码`。与其他编译语言一样,Funk首先被编译成包含程序所需指令集的字节码。 + +这个文件不是一个可执行二进制文件,而是字节码。现在我们可以使用`FVM`(Funk虚拟机)来执行字节码指令。想象一下Java的执行方式。你将项目编译成一个包含字节码的 `.jar` 文件,然后将其传递给`JVM`(Java虚拟机)。这不是剽窃,这是灵感来源。 + +```console +$ fvm out.bin +Hello World! +``` + +现在它正在运行!你的第一个程序,现在在终端上显示输出。 \ No newline at end of file diff --git a/funkylib.fk b/funkylib.fk index b0c8a92..d5d61e5 100644 --- a/funkylib.fk +++ b/funkylib.fk @@ -67,3 +67,4 @@ funk strcmp(str1: u64, str2: u64): i32 { } return 0; } + diff --git a/program.fk b/program.fk index 33f864d..c4bf3a8 100644 --- a/program.fk +++ b/program.fk @@ -1 +1,3 @@ -var test: i32 = 125 + 5; \ No newline at end of file +var str1: u8 = 0; +read str1; +write str1; diff --git a/src/Ast/Ast.hs b/src/Ast/Ast.hs index 4a567d6..411e738 100644 --- a/src/Ast/Ast.hs +++ b/src/Ast/Ast.hs @@ -30,6 +30,7 @@ data Ast | Call String [Ast] | Return Ast | Write Ast + | Read String | Malloc Ast | Free Ast | Exit Ast @@ -337,6 +338,8 @@ getKeywordArg (A Lexer.Exit : xs) = do let (value, xs') = takeUntil (== A End) xs (expr, _) <- getAst value pure (Ast.Ast.Exit expr, xs') +getKeywordArg (A (Identifier "read") : A (Identifier name) : A End : xs) = do + pure (Ast.Ast.Read name, xs) getKeywordArg _ = Nothing getNumber :: [Expr] -> Maybe Ast diff --git a/src/BuildBytecode.hs b/src/BuildBytecode.hs index 8c83810..7b2b3c9 100644 --- a/src/BuildBytecode.hs +++ b/src/BuildBytecode.hs @@ -486,6 +486,21 @@ getAllocate (Ast.Ast.Allocate name t ast) = MemoryState $ do put stock2 {bytecode = bytecode stock2 |> Ustore 2 (correspondingInt 2 (toInteger (memoryGetVarIndex name (index (memVar stock2) 0))))} getAllocate _ = return () +getRead :: Ast -> MemoryState () +getRead (Read name) = MemoryState $ do + stock <- get + put stock {bytecode = bytecode stock |> Bytecode.GetChar} + stock2 <- get + put stock2 {bytecode = bytecode stock2 |> Ustore 2 (correspondingInt 2 (toInteger (memoryGetVarIndex name (index (memVar stock2) 0))))} +getRead _ = return () + +getExit :: Ast -> MemoryState () +getExit (Ast.Ast.Exit num) = MemoryState $ do + runMemoryState (getAll num) + stock <- get + put stock {bytecode = bytecode stock |> Bytecode.Exit} +getExit _ = return () + getAll :: Ast -> MemoryState () getAll (Seq asts) = MemoryState $ do runMemoryState (getSeq (Seq asts)) @@ -521,6 +536,10 @@ getAll (Ast.Ast.Write ast) = MemoryState $ do runMemoryState (getWrite (Ast.Ast.Write ast)) getAll (Ast.Ast.Allocate name t ast) = MemoryState $ do runMemoryState (getAllocate (Ast.Ast.Allocate name t ast)) +getAll (Read name) = MemoryState $ do + runMemoryState (getRead (Read name)) +getAll (Ast.Ast.Exit num) = MemoryState $ do + runMemoryState (getExit num) getAll _ = return () bcSecToList :: S.Seq Bytecode -> [Bytecode] diff --git a/src/Bytecode.hs b/src/Bytecode.hs index a04a3b1..d334b6c 100644 --- a/src/Bytecode.hs +++ b/src/Bytecode.hs @@ -83,6 +83,8 @@ data Bytecode | Write | Allocate | GetArg + | GetChar + | Exit deriving (Show, Eq) intTypesTo8bit :: IntTypes -> [Word8] @@ -284,6 +286,8 @@ toBin Modify = [53] toBin Write = [54] toBin Allocate = [55] toBin GetArg = [56] +toBin GetChar = [57] +toBin Exit = [58] getHumanReadable :: [Bytecode] -> [Char] getHumanReadable = concatMap toHumanReadable @@ -346,6 +350,8 @@ toHumanReadable Modify = " Modify\n" toHumanReadable Write = " Write\n" toHumanReadable Allocate = " Allocate\n" toHumanReadable GetArg = " GetArg\n" +toHumanReadable GetChar = " GetChar\n" +toHumanReadable Exit = " Exit\n" bytecode :: [Bytecode] bytecode = diff --git a/src/EvalVm.hs b/src/EvalVm.hs index de2acbc..3f41009 100644 --- a/src/EvalVm.hs +++ b/src/EvalVm.hs @@ -589,6 +589,15 @@ exec (GetArg, _) = do operationAddIp a <- operationPopStack operationGetArg (getIntegral a) +exec (GetChar, _) = do + operationAddIp + a <- liftIO getChar + operationPushStack (U8 (fromIntegral (fromEnum a))) +exec (Exit, _) = do + cpu <- Operation get + a <- operationPopStack + operationSetExitCode (getIntegral a :: Int) + operationSetIp (S.length (instructions cpu)) exec _ = do cpu <- Operation get operationSetIp (ip cpu + 1) diff --git a/src/LexerVm.hs b/src/LexerVm.hs index 36e442d..26e061e 100644 --- a/src/LexerVm.hs +++ b/src/LexerVm.hs @@ -72,6 +72,8 @@ data OpCode | Write | Allocate | GetArg + | GetChar + | Exit deriving (Show, Eq, Enum) data Variable @@ -213,6 +215,10 @@ getInstruction = <$ getWord8Value 55 <|> GetArg <$ getWord8Value 56 + <|> GetChar + <$ getWord8Value 57 + <|> Exit + <$ getWord8Value 58 getVariableI :: Get Variable @@ -331,6 +337,8 @@ getCouple = do Write -> getVariableN Allocate -> getVariableN GetArg -> getVariableN + GetChar -> getVariableN + Exit -> getVariableN return (opCode, var) vmToken :: BL.ByteString -> Maybe [Instruction] diff --git a/test.sh b/test.sh index 1fbd16f..4166e9c 100755 --- a/test.sh +++ b/test.sh @@ -45,7 +45,7 @@ function test_compil() { function test_all() { file=$1 - stack run $file > /dev/null + stack run $file $4 > /dev/null stack run vm-exe out.bin > output.txt if diff -q output.txt $2 ; then @@ -66,10 +66,11 @@ function test_all() { } # Test cases -test_all "Ftests/Input1.fk" "Ftests/Output1" "Simple putstr" -test_all "Ftests/Input2.fk" "Ftests/Output2" "putnbr All cases" -test_all "Ftests/Input4.fk" "Ftests/Output4" "Simple Operations" -test_all "Ftests/Input3.fk" "Ftests/Output3" "ComplexesOperations" +test_all "Ftests/Input1.fk" "Ftests/Output1" "Simple putstr" "" +test_all "Ftests/Input2.fk" "Ftests/Output2" "putnbr All cases" "" +test_all "Ftests/Input4.fk" "Ftests/Output4" "Simple Operations" "" +test_all "Ftests/Input3.fk" "Ftests/Output3" "ComplexesOperations" "" +test_all "Ftests/Input5.fk" "Ftests/Output5" "String Operations" "funkylib.fk" echo "Tested: $((passed + failed))" echo "Passed: $passed"