dev icon indicating copy to clipboard operation
dev copied to clipboard

Function Syntax RFC

Open x87 opened this issue 10 months ago • 84 comments

Goal

  • Concise syntax for SCM functions
  • Make SCM functions easier to use by hiding some low-level details

Considerations for function syntax

Pascal-style:

function a12(name: string): string
  • easier to find using a "function" keyword
  • param declaration is consistent with the "var" block syntax

~~#### C-style:~~

string a12(string name)

~~* consistent with inline var declaration~~

Pascal-style is more consistent with the rest of the language.

Rules

  • ~~Function must be declared before first usage~~ Function are available anywhere within current scope. Functions defined inside other functions are available anywhere in that function body

  • Function body starts with the "function" keyword followed by the function signature.

  • Function name ~~prefixed with @~~ is a valid label

  • The signature includes input parameters (if any) and their types, comma-separated.

  • Input parameters, when present, must be enclosed in "()". For zero-input functions "()" is optional

  • Input parameters are followed by a return type if the function returns anything

  • Multiple return types are comma-separated, ~~and parenthesized.~~

  • Return type(s) can be prefixed with the optional keyword. Function with an optional result may return nothing.

  • Function body ends with the end keyword.

  • return keyword immediately exits the function and returns control to the calling code. See Return Semantics

    • functions with optional return type(s) can use blank return to bail out immediately while returning nothing
    • There is special logical return type. This result can only be validated in IF..THEN and can not be stored in a variable. Logical function returns true or false.
    • functions that define return type(s) must return same number of values. logical function must return a condition flag. Optional functions may return nothing.
    • When you return with some value(s), it is always a true condition for IF..THEN. Empty return is always a false condition. Value returned from a logical function defines the condition.
  • ~~Function can return one or more values using the following syntax: return <condition flag> <value1> <value2> ....~~

  • ~~return keyword in functions should not be confused with return keyword in gosubs. Function's return is always followed by some values, or true, or false using CLEO5's CLEO_RETURN_WITH~~

  • end keywords serves as an implicit return ~~with the default values matching the function signature~~ ~~using RETURN (CLEO5 is required)~~ using CLEO_RETURN

  • ~~return false is a special case that can be used in any function. It sets the condition result to false and exits the function ignoring all output variables~~

  • ~~return true is a special case that can be used in function with no return type. It sets the condition result to true.~~

Examples

Declaration

  • Function name serves as a label. it can not duplicate an existing label.
  • Functions can be declared upfront to allow calls be located before the function body (forward declarations, see below)
  • "end" represents ~~CLEO_RETURN_FAIL~~ ~~RETURN~~ CLEO_RETURN 0 ~~if there no an explicit cleo_return_* on the preceding line~~
{$CLEO}
function a0
end // implicit CLEO_RETURN 0

function a1(x: int)
end // implicit CLEO_RETURN 0

function a2(x: int, y: int)
end // implicit CLEO_RETURN 0

function a3(): int
end // implicit CLEO_RETURN 0

function a4(): int, int
end // implicit CLEO_RETURN 0

function a5(): int
    return 42 // explicit cleo_return_with true 42
end // implicit CLEO_RETURN 0

function a6(): int, int
    return 42 84 // explicit cleo_return_with true 42 84
end // implicit CLEO_RETURN 0

function a7(): int
    if 0@>0
    then
        return 42 // explicit cleo_return_with true 42
    end
end // implicit CLEO_RETURN 0

function a8()
    if 0@>0
    then
        return // explicit 0051: return
    end
end // implicit CLEO_RETURN 0

function a9(): logical
    return false // explicit cleo_return_with false
end // implicit CLEO_RETURN 0

function a10(): float
    if 0@>0
    then
        return 42.0 // explicit cleo_return_with true 42.0
    end
end // implicit CLEO_RETURN 0

function a11(): string
    if 0@>0
    then
        return 'test' // explicit cleo_return_with true 'test'
    end
end // implicit CLEO_RETURN 0

function a12(name: string): string
    return name // explicit cleo_return_with true 0@ 1@
end // implicit CLEO_RETURN 0

function a14(): int
  if 0@>0
  then return 1 // cleo_return_with true 1
  else return 0 // cleo_return_with true 0
  end
end  // implicit CLEO_RETURN 0


function a15(): int, float
  if 0@>0
  then return 1 2.0 // cleo_return_with true 1 2.0
  else return 0 0 // cleo_return_with true 0 0
  end
end // implicit CLEO_RETURN 0

Examples of logical/optional return types

function no_ret_ok
    return
end

function no_ret_error
//    return 1 // error, should not return a value
end

function ret_logical_ok: logical
    return 1
    return 0
    return 0@ == 0
end

function ret_logical_error: logical
//    return // error, should return 1 value
//    return 1 2 // error, should return 1 value
end

function ret_1_ok: int
    return 0
end

function ret_1_error: int
//    return // error, should return 1 value
//    return 1 2 // error, should return 1 value
end

function ret_2_ok: int, int
    return 0 0
end

function ret_2_error: int, int
//    return      // error, should return 2 integer values
//    return 1    // error, should return 2 integer values
end

function opt_1_ok: optional int
    return
    return 0
end

function opt_1_error: optional int
//    return 1 2 // error, should return 1 integer value
end

function opt_2_ok: optional int, int
    return
    return 1 2
end

function opt_2_error: optional int, int
//    return 1 // error, should return 2 integer values
//    return 1 2 3 // error, should return 2 integer values
end



if and
    ret_logical_ok()
    0@ = ret_1_ok()
    0@, 1@ = ret_2_ok()
    0@ = opt_1_ok()
    0@, 1@ = opt_2_ok()
then
    // ok
else
    // error
end

Calling functions

a0() // ambiguous: gosub or call?, need lookahead
a1(5) // cleo_call a1 1 5
a2(5,6) // cleo_call a2 2 5 6 

// single result
0@ = a3() // cleo_call a3 0 0@

// multiple results
0@, 1@ = a4() // cleo_call a4 0 0@ 1@

// use functions in initialization position
int x = a3() // cleo_call a3 0 0@
string name = a12("test") // cleo_call a12 1 "test" 0@ 1@

// logical functions
if a9()
then
...
end

Grammar

function := ["export" whitespace] "function" whitespace identifier "(" params ")" ( return_type1 | return_type2)
params := [ identifier ":" type ] [ "," params ]

return_type1 := [ "(" ] type [ ")" ]
return_type2 := "(" type "," types ")"
types := type [ "," types ]

var1 := [ "(" ] var [ ")" ]
var2 := "(" var "," vars ")"
vars := var [ "," vars ]

function_call := [ ( var1 | var2 ) "=" ] identifier "(" args ")"
args := ( var | const ) [ "," args ]

x87 avatar Sep 14 '23 14:09 x87