ProtoScript’s syntax and features form the backbone of its graph-based programming paradigm, enabling developers to define, manipulate, and query Prototypes within the Buffaly system. If you’re familiar with C#, ProtoScript’s syntax will feel intuitive, with a structure reminiscent of classes, methods, and attributes, but tailored for a dynamic, graph-oriented model. Unlike C#’s static, class-based approach, ProtoScript is prototype-based, allowing runtime flexibility, multiple inheritance, and graph traversal operations. This section introduces ProtoScript’s core syntax and features, providing examples to illustrate their use and drawing analogies to familiar programming concepts. We’ll cover how these elements work together to model complex relationships, with a focus on clarity for developers new to ProtoScript. For details on how primitives are represented, see NativeValuePrototypes, and revisit What Are Prototypes? for foundational context.
ProtoScript uses a C#-like syntax, with semicolons to terminate statements, curly braces {} to define blocks, and comments using // or /* */. Its constructs are designed to create and manipulate Prototypes—graph nodes that represent entities and their relationships. Below are the key syntactic elements, each explained with comparisons to C# or JavaScript to ease the transition.
Here’s a simple ProtoScript example to set the stage:
prototype City {
System.String Name = "";
State State = new State();
}
prototype State {
Collection Cities = new Collection();
}
What’s Happening?
prototype Citydefines a Prototype, like a C# class, with propertiesNameandState.prototype Statedefines another Prototype with aCitiescollection.- The syntax mirrors C#’s class and field declarations but operates on graph nodes.
ProtoScript supports C#-style comments:
- Single-line:
// This is a comment - Multi-line:
/* This is a multi-line comment */
Identifiers (e.g., Prototype names, properties, functions) follow C# conventions:
- Alphanumeric with underscores, starting with a letter or underscore (e.g.,
City,NewYork_City,_internalState). - Case-sensitive, like C#.
ProtoScript’s features are tailored for graph-based programming, enabling developers to define Prototypes, their properties, behaviors, and relationships. Below, we detail each feature, its syntax, and its role in the graph model, with examples to illustrate practical use.
Purpose: Defines a Prototype, the fundamental unit in ProtoScript, acting as both a template and an instance.
Syntax:
prototype Name : Parent1, Parent2 {
// Properties, functions, and other members
}
Details:
Nameis the Prototype’s identifier (e.g.,City).: Parent1, Parent2specifies optional parent Prototypes for multiple inheritance, like C# interfaces but inheriting properties and behaviors.- The body
{}contains properties, functions, and other members. - Unlike C# classes, Prototypes can be used directly or instantiated with
new.
C# Analogy: Similar to a class declaration, but supports multiple inheritance and runtime modification, unlike C#’s single inheritance (plus interfaces).
Example:
prototype Location {
System.String Name = "";
}
prototype City : Location {
State State = new State();
}
prototype Buffalo_City : City {
Name = "Buffalo";
}
What’s Happening?
Locationis a base Prototype with aNameproperty.Cityinherits fromLocation, adding aStateproperty.Buffalo_Cityinherits fromCity, settingNameto "Buffalo".- Graph View:
Buffalo_Cityis a node with anisaedge toCity, which links toLocation.
Purpose: Define stored (extensional) data within a Prototype, representing attributes or relationships as edges to other nodes or values.
Syntax:
Type Name = DefaultValue;
Details:
Typeis a Prototype or native type (e.g.,System.String,State).Nameis the property identifier.DefaultValueis optional, often anewinstance or literal (e.g.,"",new State()).- Properties can be fully qualified (e.g.,
City::State) to resolve conflicts in multiple inheritance, unlike C#’s implicit resolution. - Properties are graph edges, linking the Prototype to another node or value.
C# Analogy: Like fields in a class, but properties are inherently part of the graph, allowing dynamic addition and traversal.
Example:
prototype Person {
System.String Gender = "";
Location Location = new Location();
}
prototype Homer : Person {
Gender = "Male";
Location = SimpsonsHouse;
}
prototype SimpsonsHouse : Location {
System.String Address = "742 Evergreen Terrace";
}
What’s Happening?
PersondefinesGender(a native value) andLocation(a Prototype instance).HomersetsGenderto "Male" and linksLocationtoSimpsonsHouse.SimpsonsHousehas anAddressproperty.- Graph View:
Homerhas edges to"Male"andSimpsonsHouse.
Purpose: Define computed (intensional) behaviors, operating on the graph to compute results or modify relationships.
Syntax:
function Name(Parameters) : ReturnType {
// Statements
}
Details:
Nameis the function identifier.Parametersare typed, like C# method parameters.ReturnTypeis a Prototype or native type.- The body uses C#-like control flow (
if,foreach) and graph operations (e.g., property access, traversal). - Functions can modify the graph (e.g., setting properties) or compute values by traversing edges.
C# Analogy: Like methods, but designed for graph manipulation, often traversing or updating node relationships.
Example:
prototype City {
System.String Name = "";
State State = new State();
function GetStateName() : System.String {
return State.Name;
}
}
prototype State {
System.String Name = "";
}
prototype NewYork_City : City {
Name = "New York City";
}
prototype NewYork_State : State {
Name = "New York";
}
NewYork_City.State = NewYork_State;
What’s Happening?
Citydefines aGetStateNamefunction that traverses theStateproperty to return itsName.NewYork_Citylinks toNewYork_State.- Calling
NewYork_City.GetStateName()returns "New York". - Graph View: The function follows the
Stateedge to accessNewYork_State.Name.
Purpose: Attach metadata to Prototypes or functions, guiding runtime behavior, such as natural language processing or categorization.
Syntax:
[AnnotationName(Parameters)]
Details:
- Annotations are applied to Prototypes or functions, similar to C# attributes.
- Common annotations:
[Lexeme.SingularPlural("word", "plural")]maps Prototypes to natural language tokens.[SubType]marks a Prototype for dynamic categorization (detailed in later sections).[TransferFunction(Dimension)]defines transformation functions (covered later).
- Annotations are processed by the ProtoScript runtime, influencing interpretation or execution.
C# Analogy: Like [Attribute] in C#, but with a focus on graph behavior and runtime processing for tasks like NLP.
Example:
[Lexeme.SingularPlural("city", "cities")]
prototype City {
System.String Name = "";
}
What’s Happening?
- The
[Lexeme.SingularPlural]annotation linksCityto the words "city" and "cities" for natural language processing. - The runtime uses this to map text like "cities" to the
CityPrototype. - Graph View: The annotation adds metadata to the
Citynode, used during parsing.
Purpose: Tests if a Prototype satisfies a categorization condition, querying its graph structure.
Syntax:
prototype -> Type { Condition }
Details:
prototypeis the target Prototype.Typespecifies the context Prototype for the condition.Conditionis a boolean expression, often involving property checks ortypeof.- Returns
trueif the condition holds, enabling dynamic categorization.
C# Analogy: Like a LINQ Where clause, but operates on graph nodes and relationships rather than collections.
Example:
prototype City {
System.String Name = "";
State State = new State();
}
prototype State {
System.String Name = "";
}
prototype NewYork_City : City {
Name = "New York City";
}
prototype NewYork_State : State {
Name = "New York";
}
NewYork_City.State = NewYork_State;
function IsInNewYork(City city) : bool {
return city -> City { this.State.Name == "New York" };
}
What’s Happening?
IsInNewYorkchecks if aCity’sState.Nameis "New York".NewYork_City -> City { this.State.Name == "New York" }returnstrue.- Graph View: The operator traverses the
Stateedge to checkName.
Purpose: Checks if a Prototype inherits from another, verifying its position in the inheritance graph.
Syntax:
prototype typeof Type
Details:
- Returns
trueifprototypehas a direct or transitiveisarelationship toType. - Used in conditions, functions, or categorizations.
C# Analogy: Like the is operator in C#, but operates on the graph’s inheritance DAG.
Example:
prototype Location {
System.String Name = "";
}
prototype City : Location {
System.String Name = "";
}
prototype Buffalo_City : City {
Name = "Buffalo";
}
function IsCity(Prototype proto) : bool {
return proto typeof City;
}
What’s Happening?
IsCity(Buffalo_City)returnstruebecauseBuffalo_City isa City.- Graph View: The operator checks for an
isaedge fromBuffalo_CitytoCity.
Purpose: Manage lists or sets of Prototypes, representing one-to-many relationships.
Syntax:
Collection Name = new Collection();
Details:
Collectionis a built-in Prototype, likeList<T>in C#.- Methods include
Add,Remove, andCount, similar to C# collections. - Collections are graph edges to multiple nodes.
C# Analogy: Like List<T> or IEnumerable<T>, but integrated into the graph structure.
Example:
prototype State {
Collection Cities = new Collection();
}
prototype City {
System.String Name = "";
}
prototype NewYork_State : State;
prototype NewYork_City : City {
Name = "New York City";
}
NewYork_State.Cities.Add(NewYork_City);
What’s Happening?
State.Citiesis a collection linking toCitynodes.NewYork_State.Cities.Add(NewYork_City)creates an edge toNewYork_City.- Graph View:
NewYork_Statehas aCitiesedge toNewYork_City.
Purpose: Provide standard programming constructs for logic and iteration.
Syntax:
if (condition) {
// Statements
} else {
// Statements
}
foreach (Type variable in collection) {
// Statements
}
Details:
ifandforeachmirror C#’s syntax and behavior.- Conditions often involve graph queries (e.g.,
typeof,->). - Used within functions to control graph operations.
C# Analogy: Nearly identical to C#’s if and foreach, but applied to graph nodes.
Example:
prototype State {
Collection Cities = new Collection();
function CountCities() : System.Int32 {
System.Int32 count = 0;
foreach (City city in Cities) {
count = count + 1;
}
return count;
}
}
What’s Happening?
CountCitiesiterates overCities, counting nodes.- Graph View: The
foreachtraversesCitiesedges to incrementcount.
Purpose: Load other ProtoScript files and import .NET types into the ProtoScript type system.
Syntax:
// Include other ProtoScript files
include "Critic/CriticOps.pts";
include Critic/CriticOps.pts;
include Path\CriticOps.pts;
include recursive "Shared/*.pts";
// Reference a .NET assembly
reference Ontology.Simulation;
reference Ontology.Simulation Ontology.Simulation;
// Import a .NET type into a short ProtoScript alias
import Ontology.Simulation Ontology.Simulation.StringWrapper String;
// Compatibility form: import path is treated as include
import File.pts;
import Path\File.pts;
import Path/File.pts;
Details:
includeaccepts quoted and unquoted path literals.- Unquoted paths must be contiguous tokens; if the path contains whitespace, quote it.
include recursiveenables wildcard discovery under subdirectories.referenceloads an assembly, with an optional local alias.import <referenceAlias> <fullTypeName> <alias>;binds a .NET type into ProtoScript.import <path>.pts;is treated as an include-style file load for compatibility.
Example:
reference Ontology.Simulation Ontology.Simulation;
import Ontology.Simulation Ontology.Simulation.StringWrapper String;
include Critic/CriticOps.pts;
prototype MessageHolder {
String Value;
}
What’s Happening?
- The assembly is loaded under
Ontology.Simulation. StringWrapperis available asStringin ProtoScript type declarations.Critic/CriticOps.ptsis parsed as an included ProtoScript source file.
Purpose: Declare host-injected runtime objects so compilation can type-check member access before runtime values are provided.
Syntax:
extern String RuntimeMessage;
Rules:
externdeclarations are file/global scope declarations.externdeclarations cannot have an initializer.- The declared type must already be resolvable (for example via
import). - Method and property access on an
externsymbol are type-checked at compile time using the declared type. - The host runtime must inject a compatible object before executing code that uses it.
Example:
reference Ontology.Simulation Ontology.Simulation;
import Ontology.Simulation Ontology.Simulation.StringWrapper String;
extern String RuntimeMessage;
function main() : string
{
return RuntimeMessage.GetStringValue();
}
Host Integration (C#):
var compiler = new Compiler();
compiler.Initialize();
var compiled = compiler.Compile(file);
var interpreter = new NativeInterpretter(compiler);
interpreter.InsertGlobalObject("RuntimeMessage", new StringWrapper("hello from host"));
interpreter.Evaluate(compiled);What’s Happening?
- The compiler validates
RuntimeMessage.GetStringValue()against the importedStringtype. - The host injects the concrete runtime object by name before executing
main.
ProtoScript integrates seamlessly with C#:
- Native Types:
System.String,System.Int32, etc., mirror C# primitives, wrapped as NativeValuePrototypes. - Runtime Calls: Functions can invoke C# methods (e.g.,
String.Format). - Runtime Injection:
externdeclarations let the host inject compatible .NET objects while preserving compile-time checking. - Type Conversions: The runtime handles mappings between ProtoScript and C# types.
Example:
prototype City {
System.String Name = "";
function FormatName() : System.String {
return String.Format("City: {0}", Name);
}
}
What’s Happening?
FormatNameuses C#’sString.Formatto format theNameproperty.- Graph View: The function accesses the
Namenode and returns a newSystem.String.
ProtoScript’s syntax operates on a graph-based runtime:
- Nodes: Prototypes, properties, and functions are nodes with unique IDs.
- Edges: Inheritance (
isa), properties, and computed relationships link nodes. - Runtime: Manages instantiation, traversal, and execution, ensuring graph integrity.
- Operators:
->andtypeoftrigger graph queries, traversing edges to evaluate conditions. - Annotations: Guide runtime behavior, processed by the interpreter for tasks like NLP.
ProtoScript’s syntax and features provide:
- Familiarity: C#-like syntax reduces the learning curve for developers.
- Expressiveness: Properties, functions, and operators enable rich graph modeling.
- Flexibility: Runtime modifications and graph operations support dynamic systems.
- Integration: Seamless C# interoperability leverages existing tools and libraries.
To show how these features combine, consider modeling a C# variable declaration:
prototype CSharp_VariableDeclaration {
CSharp_Type Type = new CSharp_Type();
System.String VariableName = "";
CSharp_Expression Initializer = new CSharp_Expression();
function IsInitialized() : bool {
return Initializer -> CSharp_Expression { this.Value != "" };
}
}
prototype CSharp_Type {
System.String TypeName = "";
}
prototype CSharp_Expression {
System.String Value = "";
}
prototype Int_Declaration : CSharp_VariableDeclaration {
Type.TypeName = "int";
VariableName = "i";
Initializer = IntegerLiteral_0;
}
prototype IntegerLiteral_0 : CSharp_Expression {
Value = "0";
}
What’s Happening?
CSharp_VariableDeclarationdefines a Prototype with properties and a function.IsInitializeduses the->operator to check theInitializer’sValue.Int_Declarationmodelsint i = 0, linking toIntegerLiteral_0.- Graph View: Nodes connect
Int_DeclarationtoCSharp_Type(int) andIntegerLiteral_0(0). - Use Case: This could support code analysis, checking if variables are initialized.
ProtoScript’s syntax and features provide a robust foundation for graph-based programming, enabling you to define and manipulate Prototypes with ease. In the next section, we’ll explore NativeValuePrototypes, which encapsulate primitive values as graph nodes, ensuring uniformity and enabling seamless integration with the Prototype system. With these tools, you’re ready to start building dynamic, interconnected models!
Previous: What Are Prototypes? | Next: NativeValuePrototypes