Dragon banner

Dragon & Fox
Collective

Fox banner

Fewer syntax definitions

Published by Jady on 5/23/24, 11:36 PM

I've narrowed down all the syntax definitions to just a few lines of really flexible code:


IToken fieldToken = new TokenString([new ParameterValueToken("fieldTypes"), new ParameterValueToken("fieldNames")]);
IToken parameterToken = new TokenString([new ParameterValueToken("parameterTypes"), new ParameterValueToken("parameterNames")]);
IToken varArgParameterToken = new TokenString([new ParameterValueToken("varArgParameterType"), new LiteralToken("..."), new ParameterValueToken("varArgParameterName")]);
AddFunction(ContextType.Program, Functions.Program, 100, new TokenSplit(new PassToken(), new LiteralToken(";"), new EOFToken(), new ParameterExpressionToken("statements")));
AddFunction(ContextType.Program, Functions.Struct, 90, new TokenString([new ParameterValueToken("name"), new TokenSplit(new LiteralToken("{"), new LiteralToken(";"), new  LiteralToken("}"), new TokenOptions([fieldToken, new ParameterExpressionToken("functions")]))]));
AddFunction(ContextType.Program, Functions.Function, 80, new TokenString([new ParameterValueToken("returnType"), new ParameterValueToken("name"), new TokenSplit(new LiteralToken("("), new LiteralToken(","), new LiteralToken(")"), new TokenOptions([parameterToken, varArgParameterToken])), new TokenOptional(new TokenSplit(new LiteralToken("{"), new LiteralToken(";"), new LiteralToken("}"), new ParameterExpressionToken("statements")))]));

AddFunction(ContextType.Function, Functions.Declare, 100, new TokenString([new LiteralToken("Declare"), new ParameterValueToken("type"), new ParameterValueToken("name")]));
AddFunction(ContextType.Function, Functions.Define, 100, new TokenString([new ParameterValueToken("type"), new ParameterValueToken("name"), new LiteralToken("="), new ParameterExpressionToken("value")]));
AddFunction(ContextType.Function, Functions.Assign, 100, new TokenString([new ParameterExpressionToken("target"), new LiteralToken("="), new ParameterExpressionToken("value")]));
AddFunction(ContextType.Function, Functions.Return, 100, new TokenString([new LiteralToken("Return"), new TokenOptional(new ParameterExpressionToken("value"))]));
AddFunction(ContextType.Function, Functions.Add, 30, new TokenString([new ParameterExpressionToken("a"), new LiteralToken("+"), new ParameterExpressionToken("b")]));
AddFunction(ContextType.Function, Functions.LessThan, 40, new TokenString([new ParameterExpressionToken("a"), new LiteralToken("<"), new ParameterExpressionToken("b")]));
AddFunction(ContextType.Function, Functions.While, 100, new TokenString([new LiteralToken("While"), new LiteralToken("("), new ParameterExpressionToken("condition"), new LiteralToken(")"), new ParameterExpressionToken("body")]));

More functions

Published by Jady on 5/22/24, 10:57 PM

Here's a more in-depth diagram of how the compiler works, if you can understand stuff like regex:


// Source code

extern Void printf(String format, Int... values);

Foo
{
	Int a;
	Int b;
	
	Foo New(Int a, Int b)
	{
		Declare Foo this;
		this.a = a;
		this.b = b;
		Return this;
	}
	
	Int* A(Foo this)
	{
		Return this.a;
	}
};

Void main()
{
	Foo foo = Foo.New(2, 3);
	printf("Starting at %i\n", foo.A());
	
	While (foo.A() < 6)
	{
		foo.A() = foo.A() + 1;
		printf("Loop %i\n", foo.A());
	};
	
	printf("Ending at %i\n", foo.A());
	Return;
};

// Compiler first pass
// Mostly just grab all the type and function information

// Available functions
Parameter(String type, String name)
	as "$type $name";
VarArgParameter(String type, String name)
	as "$type... $name";
Body(Int index)
	as "\{ $index=lexer.Index \}";
Function(String name, Parameter[] parameters, VarArgParameter? varArgParameter, String returnType, Body? body)
	as "$returnType $name \(\) $body"
	as "$returnType $name \( $varArgParameter (,)? \) $body"
	as "$returnType $name \( $parameters (, $parameters)* (, $varArgParameter)? (,)? \) $body";
Field(String type, String name)
	as "$type $name";
Struct(String name, Field[] fields, Function[] functions)
	as "$name \{ ($fields | $functions)+ \}";
Program(Struct[] structs, Function[] functions)
	as "($structs | $functions)+";



Program(
[
	Struct("Foo", [Field("Int", "a"), Field("Int", "b")],
		[
			Function("New", [Parameter("Int", "a"), Parameter("Int", "b")], None, "Foo", Some(FOO.NEW BODY INDEX)),
			Function("A", [Parameter("Foo", "this")], None, "Int*", Some(FOO.A BODY INDEX),
		]),
],
[
	Function("printf", [Parameter("String", "format")], Some(Parameter("Int", "values")), "Void", None),
	Function("main", [], None, "Void", Some(MAIN BODY INDEX)),
]);

// Compiler second pass
// Generate any intermediary code needed to parse function bodies

// Available functions
Parameter(Type type, String name);
Body(Int index);
Function(String name, Parameter[] parameters, Parameter? varArgParameter, String returnType, Body? body);
Field(Type type, String name);
Struct(String name, Field[] fields, Function[] functions);
Program(Struct[] structs, Function[] functions);



Program(
[
	Struct("Foo", [Field(Int, "a"), Field(Int, "b")],
		[
			Function("New", [Parameter(Int, "a"), Parameter(Int, "b")], None, Foo, Some(FOO.NEW BODY INDEX)),
			Function("A", [Parameter(Foo, "this")], None, Int*, Some(FOO.A BODY INDEX),
			Function("Get_a", [Parameter(Foo, "this")], None, Int*, None),
			Function("Get_b", [Parameter(Foo, "this")], None, Int*, None),
		]),
],
[
	Function("printf", [Parameter(String, "format")], Some(Parameter(Int, "values")), Void, None),
	Function("main", [], None, Void, Some(MAIN BODY INDEX)),
	Function("Get_Foo.New", [Parameter(Type<Foo>, "type")], None, Foo.New, None),
	Function("Get_Foo.A", [Parameter(Type<Foo>, "type")], None, Foo.A, None),
	Function("Call_Foo.A", [Parameter(Foo, "this")], None, () => Foo.A(this), None),
]);

// Compiler third pass
// Now we get to the actual function definitions

// Available functions

/// Compiler functions
Define(Type type, String name, Value value)
	as "$type $name = $value";
Declare(Type type, String name)
	as "$type $name";
Assign(Value target, Value value)
	as "$target = $value";
Return(Value? value)
	as "Return $value?";
While(Expression condition, Expression body)
	as "While ( $condition ) $body";
LessThan(Value left, Value right)
	as "$left < $right";
Add(Value left, Value right)
	as "$left + $right";

/// User functions
printf(String format, Value... values);
main();

/// Compiler-generated user functions
Foo.Get_a(Foo this)
	as "$this.a";
Foo.Get_b(Foo this)
	as "$this.b";
Get_Foo.New(Type type)
	as "$type.New";
Get_Foo.A(Type type)
	as "$type.A";
Call_Foo.A(Foo this)
	as "$this.A";



Foo.New(Int a, Int b)
{
	Declare(Foo, "this");
	Assign(Foo.Get_a(this), a);
	Assign(Foo.Get_b(this), b);
	Return(Some(this));
}

Foo.A(Foo this)
{
	Return(Foo.Get_a(this));
}

Main()
{
	Define(Foo, "foo", Get_Foo.New(Foo)(2, 3));
	printf("Starting at %i", Call_Foo.A(foo)());
	
	While(LessThan(Call_Foo.A(foo)(), 6),
	{
		Assign(Call_Foo.A(foo)(), Add(Call_Foo.A(foo)(), 1));
		printf("Loop %i", Call_Foo.A(foo)());
	});
	
	printf("Ending at %i", Call_Foo.A(foo));
	Return(None);
}

Everything is functions. Even the functions.

Dot functions

Published by Jady on 5/21/24, 11:17 PM

Remember how I said everything's a function? I've been working on dot notation, which of course are also functions. But they're procedurally generated functions at compile time to keep them safe, which is... particularly goofy.


Foo
{
    Int a
    
    Int GetA(Foo this)
    {
        Return this.a
    }
}

Void Main()
{
    Declare Foo foo
    Foo.GetA(foo)
    foo.GetA()
}

becomes


Foo
{
    Int a
    
    Int GetA(Foo this)
    {
        Return Get_a(this)
    }
    
    Int* Get_a(Foo this)
    as "$0.a"
    {
        Return #access(this, a)
    }
}

(Foo => Int) Get_GetA(Type<Foo> foo)
as "$0.GetA"
{
    Return #access(foo, GetA)
}

(() => Int) Call_GetA(Foo foo)
as "$0.GetA"
{
    Return () => #access(foo.Type, GetA)(foo)
}

Void Main()
{
    Declare Foo foo
    Get_GetA(Foo)(foo)
    Call_GetA(foo)()
}

These extra generated functions only exist in the compiler though, and can't be accessed any other way.

Cetus Script

Published by Jady on 5/18/24, 6:49 PM

The "make a game > make a game engine > make a programming language" pipeline is strong. Here's what I've done in Cetus Script so far!


extern Void printf(String format, Int... values)

struct Foo
{
    Int a
    Int b
}

Void main()
{
    Foo foo = New
    foo.a = 2
    printf("Starting at %i\n", foo.a)
    
    While (foo.a < 6)
    {
        foo.a = foo.a + 1
        printf("Loop %i\n", foo.a)
    }
    
    printf("Ending at %i\n", foo.a)
    Return
}

The first really big feature of Cetus has already been implemented, at least compiler-side. Every single statement in main is a function! Foo foo = New is secretly just Declare(Foo, foo). foo.a = 2 is secretly Assign(Get(foo, a), 2). These functions just have patterns associated with them that let them be used more fluently. Patterns will also eventually be able to be used for types as well, so you can say Foo? instead of Option.

Another interesting feature is that pointers are handled automatically! foo.a returns Int* when used in Assign, but it also returns Int when used in printf! You don't get to handle reference and dereference pointers manually, but it can figure out what's usable on it's own.

Other quirks of the language include using PascalCase for things that public and camelCase for things that are private, whitespace means nothing and semicolons are optional, etc.

The next thing I'm trying to work on is something like rust's traits, but also allows you to define fields in trait objects, which you can't do with rust. Other planned features are things like properties being automatic type definitions instead of just functions.

Post-stream editor size stuff

Published by Jady on 5/6/24, 4:12 PM

When I ended the stream half the text too big or too small or out of place. Turns out I never considered scale when calculating minimum size! so text size and bounds are fixed now. Here's the grayboxed version of the "Add Component" window, which is the main tool that will allow users to connect components together into prefabs.

Previous 5 <== 1 2 3 4 5 ==> Next 5