2012-02-05 2 views
2

주로 내 자신의 학습 목적 및 재미를 위해 세미 자연 스크립트 언어를 만들려고합니다. 캐치 (catch)는 네이티브 C#에 있어야하고, 내 부분에 대한 구문 분석이나 어휘 분석이 필요하지 않기 때문에, 내가하는 일은 일반 구문 설탕을 통해 수행 할 수 있어야합니다.구문 분석을하지 않고 깨끗하고 자연스러운 스크립팅 기능

문장을 읽는 것처럼 다소 읽기 쉽기 때문에 특히 프로그래밍에 특히 유창하지 않은 사람들에게 읽기 쉽고 배우기 쉽습니다.하지만 기본 코드의 모든 기능을 사용자.

예를 들어, 완벽한 세계에서 자연 언어 (이 경우 영어)과 같습니다 실제로 스크립터는 것 거의 확실 의도 일을하기 위해이 같은 문장을 허용

When an enemy is within 10 units of player, the enemy attacks the player 

C#에서 파서와 어휘 분석기를 통해 실행되는 문자열이어야합니다. 내 목표는 내가 자연스러운 무언가를 가지고있는 것이 아니라 스크립터가 문자열을 사용하여 스크립트를 작성하는 것을 원하지 않는다는 것입니다. 스크립터가 C#에 액세스 할 수 있고 구문 강조 표시, 인텔리 센스, IDE 디버깅 등을 할 수 있기를 바랍니다. 그래서 쉽게 읽을 수있는 내용을 얻으려고 시도하지만 native C#입니다. 극복 할 수있는 방법이 없다는 두 가지 주요 장애물은 ., 쉼표 , 및 빈 방법 ()의 괄호를 제거하는 것입니다. 예를 들어,이 같은 일이 가능하지만 매우 깨끗하게 읽지 않습니다 :

// C# 
When(Enemy.Condition(Conditions.isWithinDistance(Enemy, Player, 10))), Event(Attack(Enemy, Player)) 

기간과 괄호가 많은 경우에 하나의 공백으로 대체 할 수 있기 때문에 실제로, 훨씬 더 가까이 얻을 수있는 스칼라 같은 언어를 사용. 사실 당신은 더 동축 할 수있을 것,

// Scala 
When(Enemy is WithinDistance(Player, 10)) => Then(Attack From(Enemy, Player)) 

이 위의 코드는 실제로 당신에게 설정을 처리하는 엔진을 가정하고 컴파일합니다 : 예를 들어, 위의 진술을하고 스칼라에서 다음과 같이 보일 만들 수 괄호와 쉼표를 사용합니다. 위의 예에서 구문 설탕 없이는 스칼라에서,이 같은 더 많은 것 :

// Scala (without syntactical sugar) 
When(Enemy.is(WithinDistance(Player, 10)) => Then(Attack().From(Enemy, Player)) 

결론은 내가 네이티브 C# .NET을 사용하여 첫 번째 스칼라 예처럼 뭔가에 가능한 한 가까이 할 수 있습니다. 내가 할 수있는 일이 정말로 없을 수도 있지만 더 자연스럽게 읽을 수있는 트릭을 시도해보고, 마침표, 괄호 및 쉼표를 알아낼 수 있습니다 (제외하면 의미가있는 경우 제외). 자연 언어에서도).

저는 다른 언어로 C#을 사용 해본 경험이 없으므로 C++의 매크로처럼 사용할 수있는 구문 트릭에 대해 알지 못할 수 있습니다. 매크로가 실제로 좋은 해결책이 될 수는 없겠지만, 아마도 문제가 더 커질 것이고 디버깅의 악몽이 될 것입니다.하지만 적어도 C++에서는 가능할 것입니다. C#에서도 가능한 것을 원하고 있습니까?

다음은 LINQ 및 Lambda 식을 사용하여 적은 양의 줄과 적은 기호로 동일한 작업량을 얻을 수 있으며 읽기를 영어에 가깝게 코드화 할 수 있습니다. 예를 들어 ID가있는 객체 쌍 사이에서 발생하는 세 가지 충돌의 예가 있습니다. ID가 5 인 객체로 모든 충돌을 수집 한 다음 해당 충돌을 쌍의 "첫 번째"ID로 정렬 한 다음 결과를 출력하려고합니다. 한 쌍.

struct CollisionPair : IComparable, IComparer 
{ 
    public int first; 
    public int second; 

    // Since we're sorting we'll need to write our own Comparer 
    int IComparer.Compare(object one, object two) 
    { 
     CollisionPair pairOne = (CollisionPair)one; 
     CollisionPair pairTwo = (CollisionPair)two; 

     if (pairOne.first < pairTwo.first) 
      return -1; 
     else if (pairTwo.first < pairOne.first) 
      return 1; 
     else 
      return 0; 
    } 

    // ...and our own compable 
    int IComparable.CompareTo(object two) 
    { 
     CollisionPair pairTwo = (CollisionPair)two; 

     if (this.first < pairTwo.first) 
      return -1; 
     else if (pairTwo.first < this.first) 
      return 1; 
     else 
      return 0; 
    } 
} 

static void Main(string[] args) 
{   
    List<CollisionPair> collisions = new List<CollisionPair> 
    { 
     new CollisionPair { first = 1, second = 5 }, 
     new CollisionPair { first = 2, second = 3 }, 
     new CollisionPair { first = 5, second = 4 } 
    }; 

    // In a script this would be all the code you needed, everything above 
    // would be part of the game engine 
    List<CollisionPair> sortedCollisionsWithFive = new List<CollisionPair>(); 
    foreach (CollisionPair c in collisions) 
    { 
     if (c.first == 5 || c.second == 5) 
     { 
      sortedCollisionsWithFive.Add(c); 
     } 
    } 
    sortedCollisionsWithFive.Sort(); 

    foreach (CollisionPair c in sortedCollisionsWithFive) 
    { 
     Console.WriteLine("Collision between " + c.first + 
          " and " + c.second); 
    } 
} 

그리고 지금 같은 예를 LINQ 및 람다와 함께 : 다음은 LINQ 및/또는 Lambra 표현하지 않고 이런 짓을 했을까 방법이다.이 예에서는 공지 사항 우리는 CollisionPair 모두 IComparableIComparer을 함께 모두 가지고 있지 않으며, CompareCompareTo 방법으로 구현할 필요가 없습니다 :

struct CollisionPair 
{ 
    public int first; 
    public int second; 
} 

static void Main(string[] args) 
{   
    List<CollisionPair> collisions = new List<CollisionPair> 
    { 
     new CollisionPair { first = 1, second = 5 }, 
     new CollisionPair { first = 2, second = 3 }, 
     new CollisionPair { first = 5, second = 4 } 
    }; 

    // In a script this would be all the code you needed, everything above 
    // would be part of the game engine 
    (from c in collisions 
    where (c.first == 5 || c.second == 5) 
    orderby c.first select c).ForEach(c => 
     Console.WriteLine("Collision between " + c.first + 
          " and " + c.second)); 
} 

결국 우리가 LINQ로 남겨 그리고 람다 표현은 자연어에 가깝고 게임 엔진과 스크립트 모두에 대한 코드가 훨씬 적습니다. 이러한 종류의 변경은 실제로 내가 찾고있는 것이지만 LINQ와 Lambda는 둘 다 특정 구문으로 제한됩니다. 결국에는 일반과 같은 무언가가 아닙니다.

+1

당신은 Coffeescript를 보았습니까? 구문에 대한 아이디어를 줄 수 있고 코드를 일반 JS로 구문 분석하는 방법을 볼 수 있습니다. 'food is not 'chocolate'' http://coffeescript.org/ – Jonathan

+0

@Jonathan, 나는 네이티브 C#로 작성된 것을 필요로하며, 분석이나 어휘 분석이 필요하지 않습니다. 내 질문에 세부 사항을 추가하여 명확하게 설명하겠습니다. –

+0

나는 다른 언어 임에도 불구하고 유사한 개념의 구현에 대한 정보를 제공하고 있었을 뿐이라는 질문을 이해했다. – Jonathan

답변

2

또 다른 방법은, FluentInterface "패턴"을 사용하여 같은 구현하는 것입니다 : 당신이 IsWithin이, 중, 그리고 몇 가지 인터페이스를 반환 할 때와 같은 기능을 한 경우

When(enemy).IsWithin(10.units()).Of(player).Then(enemy).Attacks(player); 

을, 당신은 쉽게 할 수 추가합니다 규칙 언어를 확장하는 새로운 확장 메소드.

예를 들어 이제 그런 기능을 살펴 보자 :

public IActiveActor Then(this ICondition condition, Actor actor) { 
    /* keep the actor, etc */ 
} 

public void Attacks(this IActiveActor who, Actor whom) { 
    /* your business logic */ 
} 

을 미래에 또 다른 기능을 구현하기 쉬운 것입니다, 코드에서 아무 것도 변경하지 않고 폭주() 말 :

public void RunAway(this IActiveActor who) { 
    /* perform runaway logic */ 
} 

그래서이 작은 추가로 당신은 쓸 수있을 것입니다 :

When(player).IsWithin(10.units()).Of(enemy).Then(player).RunAway(); 

같은 ICheckActor 같은 반환 무엇인가, 당신은 단순히 정의하는 새로운 기능에 의해 새로운 조건을 소개 할 때 가정 조건을 위해 :

public ICondition IsStrongerThan(this ICheckActor me, Actor anotherGuy) { 
    if (CompareStrength(me, anotherGuy) > 0) 
     return TrueActorCondition(me); 
    else 
     return FalseActorCondition(me); 
} 

을 그래서 지금 당신은 할 수 있습니다 :

When(player) 
    .IsWithin(10.units()).Of(enemy) 
    .And(player).IsStrongerThan(enemy) 
    .Then(player) 
    .Attacks(enemy); 

또는

When(player) 
    .IsWithin(10.units()).Of(enemy) 
    .And(enemy).IsStrongerThan(player) 
    .Then(player) 
    .RunAway(); 

을 요점은 이미 가지고있는 코드에 큰 영향을주지 않고도 언어를 향상시킬 수 있다는 것입니다.

+1

이것은 정확하게 필요한 것입니다. 고맙습니다. –

2

정직하게 말하면 나는 이것이 언어의 좋은 방향이라고 생각하지 않습니다. AppleScript를 언젠가 살펴보십시오. 그들은 자연 언어를 모방하기 위해 엄청난 고통을 겪었고, 간단한 예에서는 영어와 같이 읽는 AppleScript를 작성할 수 있습니다. 실제 사용에서는 악몽입니다. 어색하고 사용하기 까다 롭습니다. 그리고 사람들은 "설정 패턴에서 벗어나지 않고 엄청나게 제한된 영어 서브 세트를 쓰는 것"으로 매우 어려움을 겪기 때문에 배우기는 어렵습니다. 규칙적이고 예측 가능한 실제 C# 구문을 배우는 것이 더 쉽습니다.

+0

동의 100 %. 특히 영어는 끔찍하게 부정확 한 언어이므로 소프트웨어를 작성하는 데 필요한 특수성을 얻는 것을 매우 어렵게 만듭니다. OP의 예에서도 많은 어지럼이 발생합니다 (공격은 언제 발생합니까? 플레이어가 움직이면 적을 움직일 때, 다른 적을 공격 할 때, 적을 공격 할 때, 공격 한 플레이어는 누구입니까?) 명확한 기사를 이해할 수 있습니다.) –

+0

이것은 주로 실험과 재미를위한 것입니다. 과거에 필자가 함께 작업 한 회사의 스크립팅 언어를 만들었습니다. 일부는 최종 사용자를 위해 만들어졌습니다. 저는 이것을 학습 경험으로 시도하고 싶습니다. 실용적이지 않을 수 있기 때문에 어디서 볼 수 있는지 보려고합니다. –

+0

글쎄, 나는 더 이상 진행하기 전에 하루나 이틀 동안 AppleScript를 살펴보기 시작할 것입니다. 적어도, 그들의 허풍으로부터 배우십시오. – StilesCrisis

2

"네이티브 C#으로 작성되었습니다."라는 귀하의 요구 사항을 이해하지 못합니다. 왜? 아마 네이티브로 작성된 것입니다.? 나는 "평이한 영어"로 작성된 이러한 규칙을 파싱 등이없는 .NET으로 컴파일 할 수 있기 때문에 이것을 이해할 수 있습니다. 그런 다음 (아마도 C#으로 작성된) 엔진은 이러한 규칙을 사용하고 평가할 수 있습니다. 모든 .NET은 실제로 어떤 언어 개발자가 사용했는지는 중요하지 않습니다. C#을 정말 필요하지 않은 경우

지금, 우리는 "못생긴 못생긴"구문보기 "단지 추한":

우리는 예를 들어 볼 수, F 번호를 만드는 방법을 알아내는 중지 할 수 있습니다. 그것은 C# 또는 VB.NET과 같은 방식으로 .NET으로 컴파일되지만, 여러분의 것과 같은 문제를 해결하는 데 더 적합합니다. ,

When enemy (within 10<unit> player) (Then enemy attacks player) 

난 단지 5 분 소요 :

는 현재, 3 (추한보고) 예 C# 및 스칼라에서 우리를 준 내가 5 분 내 머리의 상단에서 작성하는 관리 F 번호에 하나입니다 그래서 아마 더 예뻐질 수도 있습니다. 파싱이 필요 없습니다. 언제, 이내, 그 다음에 공격은 일반적인 .NET 기능 (F #으로 작성)입니다.

[<Measure>] type unit 
type Position = int<unit> 

type Actor = 
    | Enemy of Position 
    | Player of Position 

let getPosition actor = 
    match actor with 
     | Enemy x -> x 
     | Player x -> x 

let When actor condition positiveAction = 
    if condition actor 
    then positiveAction 
    else() 

let Then actor action = action actor 

let within distance actor1 actor2 = 
    let pos1 = getPosition actor1 
    let pos2 = getPosition actor2 
    abs (pos1 - pos2) <= distance 

let attacks victim agressor = 
    printfn "%s attacks %s" (agressor.GetType().Name) (victim.GetType().Name) 

이 그것을 당신은 아마 이것은이다 C# : 을 수백 수백 줄의 코드에 작성합니다하지, 정말 : 여기

내가 그것을 가능하게하기 위해 작성했던 모든 코드 .NET의 아름다움 : 적절한 작업에 적합한 언어를 사용할 수 있습니다. 그리고 F #은 DLS를위한 훌륭한 언어입니다. (여기에 필요한 것)

P.S. 당신은 심지어 "에", "를", "에"와 같은 함수를 정의 할 수 있습니다, 등은 (이 함수는 처음 인자를 아무것도하지하지만 돌아갑니다) 영어처럼 더 보이게하기 :

let an something = something 
let the = an 
let is = an 

행운을 빕니다!

+0

이것은 매우 인상적이지만 논쟁을 위해서,이 기술은 F # 함수로 이어지기 때문에 필자가 쉽게 읽을 수 있지만, 필자가 작성하기가 쉽지는 않습니다. 완전한 구문으로 하나를 작성하려면 언어 참조와 컴퓨터 과학 정밀도가 필요합니다. 한 번 끝내면 멋지게 읽습니다. 그리고 우리는 여전히 루프 구조 또는 비교/분기와 같은 흥미로운 문제를 다루지 않았습니다. – StilesCrisis

+0

XNA를 사용하기 때문에 C#이 필요합니다. –

+0

@StilesCrisis F #은 모든 사람이 글쓰기가 쉽지 않을 수도 있습니다. 글을 쓰는 시간이 적기 때문에 글을 쓰는 것이 쉽지 않을 수도 있습니다. 그런 다음 내 다음 제안을보십시오 : 유창한 구성 :) –

관련 문제