Start supporting numbers and strings

This commit is contained in:
2023-02-25 16:45:54 +00:00
parent cbae1c4629
commit 636101f1fa
15 changed files with 379 additions and 37 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,2 @@
node_modules node_modules
build bin

3
build.bat Normal file
View File

@@ -0,0 +1,3 @@
@echo off
go build -o bin/Argon-v3.exe ./src

View File

@@ -1,11 +1,48 @@
# Argon <div align="center">
<p>
<img width="80" src="https://raw.githubusercontent.com/Ugric/Argon/main/logo.png">
</p>
<h1>Argon 3</h1>
</div>
The go to language for mathematicians! ARGON 3 is a math-driven programming language designed to make code easy to read and write. It's not meant to be fast, as it's interpreted. This specification should be used as a guideline, and is subject to change for later versions. Later updates for Argon 3 should be backwards compatible (where possible) with code designed for older versions of the interpreter.
# Read Me ## 📚 Features
due to the project being in very early stages, the readme docs file is not yet being worked on. [read the specification to get an understanding of how the language will work.](spec.md) - Easy to read and write: Argon 3 is designed with clarity of code in mind, making it easier for you and others to read and write code.
- Math-driven: Designed for mathematical computations, Argon 3 uses techniques and rules set in maths. It's designed to be easy for mathematicians to write and understand algorithms in.
- Interpreted: Argon 3 is an interpreted language, so you don't need to compile your code before running it.
- Cross-platform: Argon 3 can be run on any platform that has an interpreter for it.
- Lightweight: The Argon 3 interpreter is small and doesn't require a lot of system resources to run.
# Licence ## 💻 Installation
As of now, Argon 3 does not have an installer. Feel free to clone this repo and run the `build` file for your plateform. the build will be found in `bin/Argon-v3(.exe)`.
## 📖 Usage
To use Argon 3, you can create a file with the .ar extension and write your code in it. Then, you can run your code using the interpreter. For example, if you have a file called example.ar, you can run it using the following command:
```
argon example.ar
```
## 🔍 Specification
For a detailed specification of the Argon 3 language, please refer to [spec.md](spec.md).
## 🚀 Example Code
Here's an example of how to define a function in Argon 3:
```javascript
f(x) = x^2 + 2*x + 1
log('f(10) =', f(10))
```
This code defines a function f(x) that calculates x^2 + 2*x + 1. It then calls the function with an argument of 10 and logs the result to the console.
Please note that this example is subject to change as the specification is in beta and may be updated frequently.
## Licence
MIT MIT

5
run.bat Normal file
View File

@@ -0,0 +1,5 @@
@echo off
:: run the go run command passing the path to the main.go file, with the working directory set to the bin folder. pass in the arguments
go run ./src %*

51
spec.md
View File

@@ -24,10 +24,10 @@ reused variables, and infomation for use in understanding the pseudo REGEX:
## set variable ## set variable
`(let/const or nothing) {NAME} = {ANY}` `(let or nothing) {NAME} = {ANY}`
let and const variables will set variables. if a const is already set in that stack, it will throw an error. let variables will set variables. at the end of a opperation (e.g. if, while, or function) drop the
at the end of a opperation (e.g. if, while, or function) drop the variable stack. variable stack.
having no verb at the start suggests the program would like to edit a variables from a different stack. having no verb at the start suggests the program would like to edit a variables from a different stack.
if there is no variable found in the other stacks, then it sets one in the current stack as a let. if there is no variable found in the other stacks, then it sets one in the current stack as a let.
@@ -36,17 +36,16 @@ setting variables returns the value, which can be used.
example: example:
``` ```javascript
if (x = 10) > 5 [ if (x = 10) > 5 do
log(x, 'is bigger than 5') log(x, 'is bigger than 5')
]
``` ```
--- ---
## functions ## functions
`(let/const or nothing) {NAME}({PARAMS}) = {CODE}` `(let or nothing) {NAME}({PARAMS}) = {CODE}`
the starting verb follows the rules set by [set variable](#set-variable). the starting verb follows the rules set by [set variable](#set-variable).
@@ -54,41 +53,41 @@ function can be called by using its name followed by brackets with the params.
example: example:
``` ```javascript
f(x) = x^2 + 2*x + 1
const f(x) = x^2 + 2*x + 1
log('f(10) =', f(10)) log('f(10) =', f(10))
``` ```
output: output:
``` ```javascript
f(10) = 121 f(10) = 121
``` ```
if the function does not return, then the value given is unknown/null if the function does not return, then the value returned is `unknown`
--- ---
## wrap ## wrap
`[{CODE}]` ```
do
{CODE}
```
a wrap encloses code in square brackets. its used to create a new scope, so variables set from a wrap encloses code indented in after the word `do` its used to create a new scope, so variables set from
inside the wraps scope are deleted once the wrap is finished. inside the wraps scope are deleted once the wrap is finished.
example: example:
``` ```javascript
let name = unknown let name = unknown
[
do
name = input('name: ') name = input('name: ')
let age = input('age: ') let age = input('age: ')
log('you are', age, 'years old!') log('you are', age, 'years old!')
]
log('hello', name) log('hello', name)
log('we do not know your age anymore because it got deleted when the wrap finished.') log('we do not know your age anymore because it got deleted when the wrap finished.')
``` ```
@@ -97,17 +96,19 @@ A wrap, unless specificifed otherwise, can have a return value. This value can b
example: example:
``` ```javascript
const password = [ let password = do
let password = input("set password: ") let password = input("set password: ")
while len(password) < 8 [ while len(password) < 8 do
log("password must be longer then 8 characters!") log("password must be longer then 8 characters!")
password = input("set password: ") password = input("set password: ")
]
return password return password
]
log("your password is", password) log("your password is", password)
``` ```
If the wrap does not take a return value, then the wrap passes the return value back to a parent wrap. If the wrap does not take a return value, then the wrap passes the return value back to a parent wrap.
## Comments
`//{COMMENT}`
Comments allow the programmer to write a message into their code, without the message being processed by the computer.

7
src/comment.go Normal file
View File

@@ -0,0 +1,7 @@
package main
var commentCompile = makeRegex("( *)//.*")
func isComment(code UNPARSEcode) bool {
return commentCompile.MatchString(code.code)
}

102
src/import.go Normal file
View File

@@ -0,0 +1,102 @@
package main
import (
"bufio"
"errors"
"log"
"os"
"path/filepath"
)
func FileExists(filename string) bool {
if _, err := os.Stat(filename); err == nil {
return true
} else if errors.Is(err, os.ErrNotExist) {
return false
} else {
return false
}
}
func readFile(path string) []UNPARSEcode {
file, err := os.Open(path)
if err != nil {
log.Fatal(err)
return nil
}
defer file.Close()
scanner := bufio.NewScanner(file)
// optionally, resize scanner's capacity for lines over 64K, see next example
output := []UNPARSEcode{}
line := 1
for scanner.Scan() {
output = append(output, UNPARSEcode{scanner.Text(), line})
line++
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
return nil
}
return output
}
func importMod(realpath string, origin string, main bool) string {
extention := filepath.Ext(realpath)
path := realpath
if extention == "" {
path += ".ar"
}
ex, err := os.Getwd()
if err != nil {
return err.Error()
}
executable, err := os.Executable()
if err != nil {
return err.Error()
}
executable = filepath.Dir(executable)
isABS := filepath.IsAbs(path)
var pathsToTest []string
if isABS {
pathsToTest = []string{
filepath.Join(path),
filepath.Join(realpath, "init.ar"),
}
} else {
pathsToTest = []string{
filepath.Join(origin, realpath, "init.ar"),
filepath.Join(origin, path),
filepath.Join(origin, "modules", path),
filepath.Join(origin, "modules", realpath, "init.ar"),
filepath.Join(ex, path),
filepath.Join(ex, "modules", realpath, "init.ar"),
filepath.Join(ex, "modules", path),
filepath.Join(executable, "modules", realpath, "init.ar"),
filepath.Join(executable, "modules", path),
}
}
var p string
var found bool
for _, p = range pathsToTest {
if FileExists(p) {
found = true
break
}
}
if !found {
return "File does not exist: " + realpath
}
codelines := readFile(p)
translated, translationerr := translate(codelines)
if translationerr != "" {
return translationerr
}
run(translated)
return ""
}

View File

@@ -1,8 +1,23 @@
package main package main
import "fmt" import (
"os"
)
// args without the program path
var Args = os.Args[1:]
func main() { func main() {
translate("")
fmt.Println("hello world") ex, e := os.Getwd()
if e != nil {
panic(e)
}
if len(Args) == 0 {
panic("No file specified")
}
err := importMod(Args[0], ex, true)
if err != "" {
panic(err)
}
} }

View File

@@ -6,6 +6,8 @@ import (
"strings" "strings"
) )
var numberCompile = makeRegex("( *)((\\-)?(([0-9]*(\\.[0-9]+)?)(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)|(0b[10]+(.[10]+)?(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?)|(0x[a-fA-F0-9]+(.[a-fA-F0-9]+)?)|(0o[0-7]+(.[0-7]+)?(e((\\-|\\+)?([0-9]+(\\.[0-9]+)?)))?))( *)")
// a number type // a number type
type number = *big.Rat type number = *big.Rat
@@ -19,6 +21,10 @@ func stringToNumber(str string) (*big.Rat, bool) {
return newNumber().SetString(str) return newNumber().SetString(str)
} }
func isNumber(code UNPARSEcode) bool {
return numberCompile.MatchString(code.code)
}
// converts a number type to a string // converts a number type to a string
func numberToString(num number, fraction int) string { func numberToString(num number, fraction int) string {
if fraction != 0 { if fraction != 0 {
@@ -72,3 +78,12 @@ var subscript = map[byte]string{
'8': "₈", '8': "₈",
'9': "₉", '9': "₉",
} }
// returns translateNumber, success, error
func parseNumber(code UNPARSEcode) (translateNumber, bool, string) {
output, _ := newNumber().SetString(code.code)
return translateNumber{
number: output,
line: code.line,
}, true, ""
}

14
src/regex.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import (
"log"
"regexp"
)
func makeRegex(str string) *regexp.Regexp {
Compile, err := regexp.Compile("^(" + str + ")$")
if err != nil {
log.Fatal(err)
}
return Compile
}

24
src/run.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import "fmt"
func runLine(line any) (any, string) {
switch line.(type) {
case translateNumber:
return (numberToString(line.(translateNumber).number, 0)), ""
case translateString:
return (line.(translateString).str), ""
}
return nil, "Error: invalid code on line " + fmt.Sprint(line.(translateNumber).line) + ": " + line.(translateNumber).code
}
// returns error
func run(translated []any) (any, string) {
for _, val := range translated {
_, err := runLine(val)
if err != "" {
return nil, err
}
}
return nil, ""
}

42
src/string.go Normal file
View File

@@ -0,0 +1,42 @@
package main
import (
"fmt"
"strconv"
"strings"
)
var stringCompile = makeRegex("(( *)\"((\\\\([a-z\\\"'`]))|[^\\\"])*\"( *))|(( *)'((\\\\([a-z\\'\"`]))|[^\\'])*'( *))")
func isString(code UNPARSEcode) bool {
return stringCompile.MatchString(code.code)
}
func unquoted(
str string,
) (string, error) {
str = strings.Trim(str, " ")
if str[0] == '\'' {
str = strings.Replace(str, "\\\"", "\"", -1)
str = strings.Replace(str, "\"", "\\\"", -1)
}
str = str[1 : len(str)-1]
str = strings.Replace(str, "\\'", "'", -1)
str = "\"" + str + "\""
return strconv.Unquote(str)
}
// returns translateString, success, error
func parseString(code UNPARSEcode) (translateString, bool, string) {
trim := strings.Trim(code.code, " ")
unquoted, err := unquoted(trim)
if err != nil {
return translateString{}, false, "Syntax Error: invalid string on line " + fmt.Sprint(code.line) + ": " + code.code
}
return translateString{
str: unquoted,
line: code.line,
}, true, ""
}

View File

@@ -2,9 +2,42 @@ package main
import ( import (
"fmt" "fmt"
"log"
) )
func translate(code string) { // returns (translateNumber | translateString), success, error
output, _ := newNumber().SetString("3.1415") func translateVal(code UNPARSEcode, index int, codelines []UNPARSEcode, isLine bool) (any, bool, string) {
fmt.Println(numberToString(output, 0)) if isLine {
if isComment(code) {
return nil, true, ""
}
}
if isNumber(code) {
return parseNumber(code)
} else if isString(code) {
return parseString(code)
}
if isLine {
return nil, false, "Syntax Error: invalid code on line " + fmt.Sprint(code.line) + ": " + code.code
}
return nil, false, ""
}
// returns [](translateNumber | translateString), error
func translate(codelines []UNPARSEcode) ([]any, string) {
translated := []any{}
for i, code := range codelines {
val, _, err := translateVal(code, i, codelines, true)
if err != "" {
log.Fatal(err)
return nil, err
}
if val == nil {
continue
}
translated = append(translated, val)
}
return translated, ""
} }

18
src/types.go Normal file
View File

@@ -0,0 +1,18 @@
package main
type UNPARSEcode struct {
code string
line int
}
type translateNumber struct {
number
code string
line int
}
type translateString struct {
str string
code string
line int
}

26
test.ar Normal file
View File

@@ -0,0 +1,26 @@
0x1
0x2
0x3
0x4
0x5
0x6
0x7
0x8
0x9
0xA
0xB
0xC
0xD
0xE
0xF
0x10
0x11
0x12
0x13
0x14
0x15
0x16
0x17
0x18
0x19
0x1A